More codebase cleanups.

git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@246 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
stephena 2004-05-24 17:32:45 +00:00
parent 469f00eefc
commit 8b5bdd2340
21 changed files with 0 additions and 6287 deletions

View File

@ -1,188 +0,0 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-1999 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Snapshot.cxx,v 1.9 2003-12-06 00:17:28 stephena Exp $
//============================================================================
#include <png.h>
#include <iostream>
#include <fstream>
#include "bspf.hxx"
#include "FrameBuffer.hxx"
#include "Console.hxx"
#include "MediaSrc.hxx"
#include "Snapshot.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Snapshot::Snapshot(Console* console, MediaSource* mediasrc)
: myConsole(console),
myMediaSource(mediasrc),
palette((png_colorp) NULL)
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Snapshot::~Snapshot()
{
if(palette)
delete[] palette;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Snapshot::png_write_data(png_structp ctx, png_bytep area, png_size_t size)
{
ofstream* out = (ofstream *) png_get_io_ptr(ctx);
out->write((const char *)area, size);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Snapshot::png_io_flush(png_structp ctx)
{
ofstream* out = (ofstream *) png_get_io_ptr(ctx);
out->flush();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Snapshot::png_user_warn(png_structp ctx, png_const_charp str)
{
cerr << "Snapshot: libpng warning: " << str << endl;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Snapshot::png_user_error(png_structp ctx, png_const_charp str)
{
cerr << "Snapshot: libpng error: " << str << endl;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 Snapshot::savePNG(string filename, uInt32 multiplier)
{
// FIXME - we shouldn't use the mediasource, but should instead use
// the framebuffer.
// Right now, the snapshot doesn't take into account any special effects
// that the framebuffer may be performing (aspect correction, OpenGL, etc)
// This will require framebuffer support for exporting its actual pixels,
// so it will have to wait ...
png_structp png_ptr = 0;
png_infop info_ptr = 0;
uInt8* pixels = myMediaSource->currentFrameBuffer();
// PNG and window dimensions will be different because of scaling
uInt32 picWidth = myMediaSource->width() * multiplier << 1;
uInt32 picHeight = myMediaSource->height() * multiplier;
uInt32 width = myMediaSource->width();
uInt32 height = myMediaSource->height();
ofstream* out = new ofstream(filename.c_str(), ios_base::binary);
if(!out)
return 0;
// Create the palette if it hasn't previously been set
if(!palette)
{
palette = (png_colorp) new png_color[256];
if(!palette)
{
cerr << "Snapshot: Couldn't allocate memory for PNG palette\n";
out->close();
return 0;
}
const uInt32* gamePalette = myMediaSource->palette();
for(uInt32 i = 0; i < 256; ++i)
{
palette[i].red = (uInt8) ((gamePalette[i] & 0x00ff0000) >> 16);
palette[i].green = (uInt8) ((gamePalette[i] & 0x0000ff00) >> 8);
palette[i].blue = (uInt8) (gamePalette[i] & 0x000000ff);
}
}
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, png_user_error, png_user_warn);
if(png_ptr == NULL)
{
cerr << "Snapshot: Couldn't allocate memory for PNG file\n";
return 0;
}
// Allocate/initialize the image information data. REQUIRED.
info_ptr = png_create_info_struct(png_ptr);
if(info_ptr == NULL)
{
cerr << "Snapshot: Couldn't create image information for PNG file\n";
png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
out->close();
return 0;
}
png_set_write_fn(png_ptr, out, png_write_data, png_io_flush);
png_set_IHDR(png_ptr, info_ptr, picWidth, picHeight, 8,
PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
// Set the 8-bit palette
png_set_PLTE(png_ptr, info_ptr, palette, 256);
// Write the file header information. REQUIRED
png_write_info(png_ptr, info_ptr);
// Pack pixels into bytes
png_set_packing(png_ptr);
// The width has to be scaled by 2 * multiplier. Each pixel must be
// present scaleX times. Each scanline must be present scaleY times.
uInt32 scaleX = multiplier << 1;
uInt32 scaleY = multiplier;
// Create a buffer to hold the new scanline.
uInt8* newScanline = new uInt8[width * scaleX];
uInt8* oldScanline;
// Look at each original scanline
for(uInt32 y = 0; y < height; y++)
{
// First construct a new scanline that is scaled
oldScanline = (uInt8*) pixels + y*width;
uInt32 px = 0;
for(uInt32 x = 0; x < width; x++)
for(uInt32 offx = 0; offx < scaleX; offx++)
newScanline[px++] = oldScanline[x];
// Now output the new scanline 'scaleY' times
for(uInt32 offy = 0; offy < scaleY; offy++)
{
png_bytep row_pointer = (uInt8*) newScanline;
png_write_row(png_ptr, row_pointer);
}
}
png_write_end(png_ptr, info_ptr);
png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
if(newScanline)
delete[] newScanline;
out->close();
delete out;
return 1;
}

View File

@ -1,75 +0,0 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-1999 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Snapshot.hxx,v 1.5 2003-11-19 15:57:10 stephena Exp $
//============================================================================
#ifndef SNAPSHOT_HXX
#define SNAPSHOT_HXX
class Console;
class MediaSource;
#include <png.h>
#include "bspf.hxx"
class Snapshot
{
public:
/**
Create a new shapshot class for taking snapshots in PNG format.
@param console The console
@param mediasrc The mediasource
*/
Snapshot(Console* console, MediaSource* mediasrc);
/**
The destructor.
*/
~Snapshot();
/**
This routine saves the current frame buffer to a PNG file,
appropriately scaled by the amount specified in 'multiplier'.
@param filename The filename of the PNG file
@param multiplier The amount that multiplication (zoom level)
@return The resulting error code
*/
uInt32 savePNG(string filename, uInt32 multiplier = 1);
private:
static void png_write_data(png_structp ctx, png_bytep area, png_size_t size);
static void png_io_flush(png_structp ctx);
static void png_user_warn(png_structp ctx, png_const_charp str);
static void png_user_error(png_structp ctx, png_const_charp str);
private:
// The Console for the system
Console* myConsole;
// The Mediasource for the system
MediaSource* myMediaSource;
// The PNG palette
png_colorp palette;
};
#endif

View File

@ -1,537 +0,0 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-1999 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: FrameBufferGL.cxx,v 1.19 2004-04-27 00:50:52 stephena Exp $
//============================================================================
#include <SDL.h>
#include <SDL_syswm.h>
#include <sstream>
#include "Console.hxx"
#include "FrameBuffer.hxx"
#include "FrameBufferSDL.hxx"
#include "FrameBufferGL.hxx"
#include "MediaSrc.hxx"
#include "Settings.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FrameBufferGL::FrameBufferGL()
: myTexture(0),
myFilterParam(GL_NEAREST)
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FrameBufferGL::~FrameBufferGL()
{
if(myTexture)
SDL_FreeSurface(myTexture);
glDeleteTextures(1, &myTextureID);
glDeleteTextures(256, myFontTextureID);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameBufferGL::createScreen()
{
SDL_GL_SetAttribute( SDL_GL_RED_SIZE, myRGB[0] );
SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, myRGB[1] );
SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, myRGB[2] );
SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, myRGB[3] );
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
GLint viewportX = 0, viewportY = 0;
uInt32 screenWidth = 0;
uInt32 screenHeight = 0;
uInt32 imageWidth = (uInt32) (myWidth * theZoomLevel * theAspectRatio);
uInt32 imageHeight = myHeight * theZoomLevel;
// Determine if we're in fullscreen or windowed mode
// In fullscreen mode, we clip the SDL screen to known resolutions
// In windowed mode, we use the actual image resolution for the SDL screen
if(mySDLFlags & SDL_FULLSCREEN)
{
SDL_Rect rect = viewport(imageWidth, imageHeight);
viewportX = rect.x;
viewportY = rect.y;
screenWidth = rect.w - 1;
screenHeight = rect.h - 1;
}
else
{
screenWidth = imageWidth;
screenHeight = imageHeight;
viewportX = viewportY = 0;
}
myScreen = SDL_SetVideoMode(screenWidth, screenHeight, 0, mySDLFlags);
if(myScreen == NULL)
{
cerr << "ERROR: Unable to open SDL window: " << SDL_GetError() << endl;
return false;
}
glPushAttrib(GL_ENABLE_BIT);
// Center the screen horizontally and vertically
glViewport(viewportX, viewportY, imageWidth, imageHeight);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0.0, (GLdouble) imageWidth/(theZoomLevel * theAspectRatio),
(GLdouble) imageHeight/theZoomLevel, 0.0, 0.0, 1.0);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
#ifdef TEXTURES_ARE_LOST
createTextures();
#endif
// Make sure any old parts of the screen are erased
glClear(GL_COLOR_BUFFER_BIT);
theRedrawEntireFrameIndicator = true;
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameBufferGL::init()
{
// Get the desired width and height of the display
myWidth = myMediaSource->width() << 1;
myHeight = myMediaSource->height();
// Get the aspect ratio for the display
// Since the display is already doubled horizontally, we half the
// ratio that is provided
theAspectRatio = myConsole->settings().getFloat("gl_aspect") / 2;
if(theAspectRatio <= 0.0)
theAspectRatio = 1.0;
// Now create the OpenGL SDL screen
Uint32 initflags = SDL_INIT_VIDEO | SDL_INIT_TIMER;
if(SDL_Init(initflags) < 0)
return false;
// Check which system we are running under
x11Available = false;
#if UNIX && (!__APPLE__)
SDL_VERSION(&myWMInfo.version);
if(SDL_GetWMInfo(&myWMInfo) > 0)
if(myWMInfo.subsystem == SDL_SYSWM_X11)
x11Available = true;
#endif
// Get the maximum size of a window for THIS screen
theMaxZoomLevel = maxWindowSizeForScreen();
// Check to see if window size will fit in the screen
if((uInt32)myConsole->settings().getInt("zoom") > theMaxZoomLevel)
theZoomLevel = theMaxZoomLevel;
else
theZoomLevel = myConsole->settings().getInt("zoom");
mySDLFlags = SDL_OPENGL;
mySDLFlags |= myConsole->settings().getBool("fullscreen") ? SDL_FULLSCREEN : 0;
// Set the window title and icon
setWindowAttributes();
// Set up the OpenGL attributes
myDepth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
switch(myDepth)
{
case 8:
myRGB[0] = 3;
myRGB[1] = 3;
myRGB[2] = 2;
myRGB[3] = 0;
break;
case 15:
myRGB[0] = 5;
myRGB[1] = 5;
myRGB[2] = 5;
myRGB[3] = 0;
break;
case 16:
myRGB[0] = 5;
myRGB[1] = 6;
myRGB[2] = 5;
myRGB[3] = 0;
break;
case 24:
myRGB[0] = 8;
myRGB[1] = 8;
myRGB[2] = 8;
myRGB[3] = 0;
break;
case 32:
myRGB[0] = 8;
myRGB[1] = 8;
myRGB[2] = 8;
myRGB[3] = 8;
break;
default: // This should never happen
break;
}
// Create the screen
if(!createScreen())
return false;
// Now check to see what color components were actually created
SDL_GL_GetAttribute( SDL_GL_RED_SIZE, (int*)&myRGB[0] );
SDL_GL_GetAttribute( SDL_GL_GREEN_SIZE, (int*)&myRGB[1] );
SDL_GL_GetAttribute( SDL_GL_BLUE_SIZE, (int*)&myRGB[2] );
SDL_GL_GetAttribute( SDL_GL_ALPHA_SIZE, (int*)&myRGB[3] );
#ifndef TEXTURES_ARE_LOST
// Create the texture surface and texture fonts
createTextures();
#endif
// Set up the palette *after* we know the color components
// and the textures
setupPalette();
// Show some OpenGL info
if(myConsole->settings().getBool("showinfo"))
{
ostringstream colormode;
colormode << "Color : " << myDepth << " bit, " << myRGB[0] << "-"
<< myRGB[1] << "-" << myRGB[2] << "-" << myRGB[3];
cout << endl
<< "Vendor : " << glGetString(GL_VENDOR) << endl
<< "Renderer: " << glGetString(GL_RENDERER) << endl
<< "Version : " << glGetString(GL_VERSION) << endl
<< colormode.str() << endl << endl;
}
// Make sure that theUseFullScreenFlag sets up fullscreen mode correctly
if(myConsole->settings().getBool("fullscreen"))
{
grabMouse(true);
showCursor(false);
}
else
{
// Keep mouse in game window if grabmouse is selected
grabMouse(myConsole->settings().getBool("grabmouse"));
// Show or hide the cursor depending on the 'hidecursor' argument
showCursor(!myConsole->settings().getBool("hidecursor"));
}
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferGL::drawMediaSource()
{
// Copy the mediasource framebuffer to the RGB texture
uInt8* currentFrame = myMediaSource->currentFrameBuffer();
uInt8* previousFrame = myMediaSource->previousFrameBuffer();
uInt32 width = myMediaSource->width();
uInt32 height = myMediaSource->height();
uInt16* buffer = (uInt16*) myTexture->pixels;
register uInt32 y;
for(y = 0; y < height; ++y )
{
const uInt32 bufofsY = y * width;
const uInt32 screenofsY = y * myTexture->w;
register uInt32 x;
for(x = 0; x < width; ++x )
{
const uInt32 bufofs = bufofsY + x;
uInt8 v = currentFrame[bufofs];
if(v == previousFrame[bufofs] && !theRedrawEntireFrameIndicator)
continue;
// x << 1 is times 2 ( doubling width )
const uInt32 pos = screenofsY + (x << 1);
buffer[pos] = buffer[pos+1] = (uInt16) myPalette[v];
}
}
// Texturemap complete texture to surface so we have free scaling
// and antialiasing
glBindTexture(GL_TEXTURE_2D, myTextureID);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, myTexture->w, myTexture->h,
GL_RGB, GL_UNSIGNED_SHORT_5_6_5, myTexture->pixels);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
glColor3f(0.0, 0.0, 0.0);
glBegin(GL_QUADS);
glTexCoord2f(myTexCoord[0], myTexCoord[1]); glVertex2i(0, 0);
glTexCoord2f(myTexCoord[2], myTexCoord[1]); glVertex2i(myWidth, 0);
glTexCoord2f(myTexCoord[2], myTexCoord[3]); glVertex2i(myWidth, myHeight);
glTexCoord2f(myTexCoord[0], myTexCoord[3]); glVertex2i(0, myHeight);
glEnd();
// The frame doesn't need to be completely redrawn anymore
theRedrawEntireFrameIndicator = false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferGL::preFrameUpdate()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferGL::postFrameUpdate()
{
// Now show all changes made to the textures
SDL_GL_SwapBuffers();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferGL::drawBoundedBox(uInt32 x, uInt32 y, uInt32 w, uInt32 h)
{
// First draw the box in the background, alpha-blended
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND);
glColor4f(0.0, 0.0, 0.0, 0.7);
glRecti(x, y, x+w, y+h);
// Now draw the outer edges
glLineWidth(theZoomLevel/2);
glColor4f(0.8, 0.8, 0.8, 1.0);
glBegin(GL_LINE_LOOP);
glVertex2i(x, y ); // Top Left
glVertex2i(x+w, y ); // Top Right
glVertex2i(x+w, y+h); // Bottom Right
glVertex2i(x, y+h); // Bottom Left
glEnd();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferGL::drawText(uInt32 x, uInt32 y, const string& message)
{
for(uInt32 i = 0; i < message.length(); i++)
drawChar(x + i*8, y, (uInt32) message[i]);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferGL::drawChar(uInt32 x, uInt32 y, uInt32 c)
{
if(c >= 256 )
return;
glBindTexture(GL_TEXTURE_2D, myFontTextureID[c]);
glBegin(GL_QUADS);
glTexCoord2f(0, 0); glVertex2i(x, y );
glTexCoord2f(1, 0); glVertex2i(x+8, y );
glTexCoord2f(1, 1); glVertex2i(x+8, y+8);
glTexCoord2f(0, 1); glVertex2i(x, y+8);
glEnd();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameBufferGL::createTextures()
{
if(myTexture)
SDL_FreeSurface(myTexture);
glDeleteTextures(1, &myTextureID);
glDeleteTextures(256, myFontTextureID);
uInt32 w = power_of_two(myWidth);
uInt32 h = power_of_two(myHeight);
myTexCoord[0] = 0.0f;
myTexCoord[1] = 0.0f;
myTexCoord[2] = (GLfloat) myWidth / w;
myTexCoord[3] = (GLfloat) myHeight / h;
myTexture = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 16,
0x0000F800, 0x000007E0, 0x0000001F, 0x00000000);
if(myTexture == NULL)
return false;
// Create an OpenGL texture from the SDL texture
bool showinfo = myConsole->settings().getBool("showinfo");
string filter = myConsole->settings().getString("gl_filter");
if(filter == "linear")
{
myFilterParam = GL_LINEAR;
if(showinfo)
cout << "Using GL_LINEAR filtering.\n";
}
else if(filter == "nearest")
{
myFilterParam = GL_NEAREST;
if(showinfo)
cout << "Using GL_NEAREST filtering.\n";
}
glGenTextures(1, &myTextureID);
glBindTexture(GL_TEXTURE_2D, myTextureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, myFilterParam);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, myFilterParam);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5,
myTexture->pixels);
// Now create the font textures. There are 256 fonts of 8x8 pixels.
// These will be stored in 256 textures of size 8x8.
SDL_Surface* fontTexture = SDL_CreateRGBSurface(SDL_SWSURFACE, 8, 8, 32,
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
#else
0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF);
#endif
if(fontTexture == NULL)
return false;
// Create a texture for each character
glGenTextures(256, myFontTextureID);
for(uInt32 c = 0; c < 256; c++)
{
// First clear the texture
SDL_Rect tmp;
tmp.x = 0; tmp.y = 0; tmp.w = 8; tmp.h = 8;
SDL_FillRect(fontTexture, &tmp,
SDL_MapRGBA(fontTexture->format, 0xff, 0xff, 0xff, 0x0));
// Now fill the texture with font data
for(uInt32 y = 0; y < 8; y++)
{
for(uInt32 x = 0; x < 8; x++)
{
if((ourFontData[(c << 3) + y] >> x) & 1)
{
tmp.x = x;
tmp.y = y;
tmp.w = tmp.h = 1;
SDL_FillRect(fontTexture, &tmp,
SDL_MapRGBA(fontTexture->format, 0x10, 0x10, 0x10, 0xff));
}
}
}
glBindTexture(GL_TEXTURE_2D, myFontTextureID[c]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, myFilterParam);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, myFilterParam);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE,
fontTexture->pixels);
}
SDL_FreeSurface(fontTexture);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_TEXTURE_2D);
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferGL::toggleFilter()
{
if(myFilterParam == GL_NEAREST)
{
myFilterParam = GL_LINEAR;
myConsole->settings().setString("gl_filter", "linear");
showMessage("GL_LINEAR filtering");
}
else
{
myFilterParam = GL_NEAREST;
myConsole->settings().setString("gl_filter", "nearest");
showMessage("GL_NEAREST filtering");
}
glBindTexture(GL_TEXTURE_2D, myTextureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, myFilterParam);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, myFilterParam);
for(uInt32 i = 0; i < 256; i++)
{
glBindTexture(GL_TEXTURE_2D, myFontTextureID[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, myFilterParam);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, myFilterParam);
}
// The filtering has changed, so redraw the entire screen
theRedrawEntireFrameIndicator = true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SDL_Rect FrameBufferGL::viewport(uInt32 width, uInt32 height)
{
SDL_Rect rect;
rect.x = rect.y = 0;
rect.w = width; rect.h = height;
struct Screenmode
{
uInt32 w;
uInt32 h;
};
// List of valid fullscreen OpenGL modes
Screenmode myScreenmode[6] = {
{320, 240 },
{640, 480 },
{800, 600 },
{1024, 768 },
{1280, 1024},
{1600, 1200}
};
for(uInt32 i = 0; i < 6; i++)
{
if(width <= myScreenmode[i].w && height <= myScreenmode[i].h)
{
rect.x = (myScreenmode[i].w - width) / 2;
rect.y = (myScreenmode[i].h - height) / 2;
rect.w = myScreenmode[i].w;
rect.h = myScreenmode[i].h;
return rect;
}
}
// If we get here, it probably indicates an error
// But we have to return something ...
return rect;
}

View File

@ -1,169 +0,0 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-1999 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: FrameBufferGL.hxx,v 1.9 2004-04-15 22:52:43 stephena Exp $
//============================================================================
#ifndef FRAMEBUFFER_GL_HXX
#define FRAMEBUFFER_GL_HXX
#include <SDL.h>
#include <SDL_opengl.h>
#include <SDL_syswm.h>
#include "FrameBuffer.hxx"
#include "FrameBufferSDL.hxx"
#include "bspf.hxx"
class Console;
class MediaSource;
/**
This class implements an SDL OpenGL framebuffer.
@author Stephen Anthony
@version $Id: FrameBufferGL.hxx,v 1.9 2004-04-15 22:52:43 stephena Exp $
*/
class FrameBufferGL : public FrameBufferSDL
{
public:
/**
Creates a new SDL OpenGL framebuffer
*/
FrameBufferGL();
/**
Destructor
*/
virtual ~FrameBufferGL();
/**
Switches between the two filtering options in OpenGL.
Currently, these are GL_NEAREST and GL_LINEAR.
*/
void toggleFilter();
//////////////////////////////////////////////////////////////////////
// The following methods are derived from FrameBufferSDL.hxx
//////////////////////////////////////////////////////////////////////
/**
This routine is called whenever the screen needs to be recreated.
It updates the global screen variable.
*/
virtual bool createScreen();
/**
This routine is called to map a given r,g,b triple to the screen palette.
@param r The red component of the color.
@param g The green component of the color.
@param b The blue component of the color.
*/
virtual Uint32 mapRGB(Uint8 r, Uint8 g, Uint8 b)
{ return SDL_MapRGB(myTexture->format, r, g, b); }
//////////////////////////////////////////////////////////////////////
// The following methods are derived from FrameBuffer.hxx
//////////////////////////////////////////////////////////////////////
/**
This routine should be called once the console is created to setup
the video system for us to use. Return false if any operation fails,
otherwise return true.
*/
virtual bool init();
/**
This routine should be called anytime the MediaSource needs to be redrawn
to the screen.
*/
virtual void drawMediaSource();
/**
This routine should be called to draw a rectangular box with sides
at the specified coordinates.
@param x The x coordinate
@param y The y coordinate
@param w The width of the box
@param h The height of the box
*/
virtual void drawBoundedBox(uInt32 x, uInt32 y, uInt32 w, uInt32 h);
/**
This routine should be called to draw text at the specified coordinates.
@param x The x coordinate
@param y The y coordinate
@param message The message text
*/
virtual void drawText(uInt32 x, uInt32 y, const string& message);
/**
This routine should be called to draw character 'c' at the specified coordinates.
@param x The x coordinate
@param y The y coordinate
@param c The character to draw
*/
virtual void drawChar(uInt32 x, uInt32 y, uInt32 c);
/**
This routine is called before any drawing is done (per-frame).
*/
virtual void preFrameUpdate();
/**
This routine is called after any drawing is done (per-frame).
*/
virtual void postFrameUpdate();
private:
bool createTextures();
SDL_Rect viewport(uInt32 width, uInt32 height);
uInt32 power_of_two(uInt32 input)
{
uInt32 value = 1;
while( value < input )
value <<= 1;
return value;
}
private:
// The main texture buffer
SDL_Surface* myTexture;
// The depth of the texture buffer
uInt32 myDepth;
// The size of color components for OpenGL
uInt32 myRGB[4];
// The OpenGL main texture handle
GLuint myTextureID;
// OpenGL texture coordinates for the main surface
GLfloat myTexCoord[4];
// The OpenGL font texture handles (one for each character)
GLuint myFontTextureID[256];
// The texture filtering to use
GLint myFilterParam;
};
#endif

View File

@ -1,268 +0,0 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-1999 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: FrameBufferSDL.cxx,v 1.14 2004-04-26 12:49:46 stephena Exp $
//============================================================================
#include <SDL.h>
#include <SDL_syswm.h>
#include <sstream>
#include "Console.hxx"
#include "FrameBuffer.hxx"
#include "FrameBufferSDL.hxx"
#include "MediaSrc.hxx"
#include "Settings.hxx"
#include "stella.xpm" // The Stella icon
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FrameBufferSDL::FrameBufferSDL()
: x11Available(false),
theZoomLevel(1),
theMaxZoomLevel(1),
theAspectRatio(1.0),
myPauseStatus(false)
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FrameBufferSDL::~FrameBufferSDL()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSDL::pauseEvent(bool status)
{
myPauseStatus = status;
setupPalette();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSDL::setupPalette()
{
// Shade the palette to 75% normal value in pause mode
float shade = 1.0;
if(myPauseStatus)
shade = 0.75;
const uInt32* gamePalette = myMediaSource->palette();
for(uInt32 i = 0; i < 256; ++i)
{
Uint8 r, g, b;
r = (Uint8) (((gamePalette[i] & 0x00ff0000) >> 16) * shade);
g = (Uint8) (((gamePalette[i] & 0x0000ff00) >> 8) * shade);
b = (Uint8) ((gamePalette[i] & 0x000000ff) * shade);
myPalette[i] = mapRGB(r, g, b);
}
theRedrawEntireFrameIndicator = true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSDL::toggleFullscreen()
{
bool isFullscreen = !myConsole->settings().getBool("fullscreen");
// Update the settings
myConsole->settings().setBool("fullscreen", isFullscreen);
if(isFullscreen)
mySDLFlags |= SDL_FULLSCREEN;
else
mySDLFlags &= ~SDL_FULLSCREEN;
if(!createScreen())
return;
if(isFullscreen) // now in fullscreen mode
{
grabMouse(true);
showCursor(false);
}
else // now in windowed mode
{
grabMouse(myConsole->settings().getBool("grabmouse"));
showCursor(!myConsole->settings().getBool("hidecursor"));
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSDL::resize(int mode)
{
// reset size to that given in properties
// this is a special case of allowing a resize while in fullscreen mode
if(mode == 0)
{
myWidth = myMediaSource->width() << 1;
myHeight = myMediaSource->height();
}
else if(mode == 1) // increase size
{
if(myConsole->settings().getBool("fullscreen"))
return;
if(theZoomLevel == theMaxZoomLevel)
theZoomLevel = 1;
else
theZoomLevel++;
}
else if(mode == -1) // decrease size
{
if(myConsole->settings().getBool("fullscreen"))
return;
if(theZoomLevel == 1)
theZoomLevel = theMaxZoomLevel;
else
theZoomLevel--;
}
if(!createScreen())
return;
// Update the settings
myConsole->settings().setInt("zoom", theZoomLevel);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSDL::showCursor(bool show)
{
if(show)
SDL_ShowCursor(SDL_ENABLE);
else
SDL_ShowCursor(SDL_DISABLE);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSDL::grabMouse(bool grab)
{
if(grab)
SDL_WM_GrabInput(SDL_GRAB_ON);
else
SDL_WM_GrabInput(SDL_GRAB_OFF);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameBufferSDL::fullScreen()
{
return myConsole->settings().getBool("fullscreen");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 FrameBufferSDL::maxWindowSizeForScreen()
{
if(!x11Available)
return 4;
#ifdef UNIX
// Otherwise, lock the screen and get the width and height
myWMInfo.info.x11.lock_func();
Display* theX11Display = myWMInfo.info.x11.display;
myWMInfo.info.x11.unlock_func();
int screenWidth = DisplayWidth(theX11Display, DefaultScreen(theX11Display));
int screenHeight = DisplayHeight(theX11Display, DefaultScreen(theX11Display));
uInt32 multiplier = screenWidth / myWidth;
bool found = false;
while(!found && (multiplier > 0))
{
// Figure out the desired size of the window
int width = (int) (myWidth * multiplier * theAspectRatio);
int height = myHeight * multiplier;
if((width < screenWidth) && (height < screenHeight))
found = true;
else
multiplier--;
}
if(found)
return multiplier;
else
return 1;
#endif
return 4;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSDL::setWindowAttributes()
{
// Set the window title
ostringstream name;
name << "Stella: \"" << myConsole->properties().get("Cartridge.Name") << "\"";
SDL_WM_SetCaption(name.str().c_str(), "stella");
#ifndef MAC_OSX
// Set the window icon
uInt32 w, h, ncols, nbytes;
uInt32 rgba[256], icon[32 * 32];
uInt8 mask[32][4];
sscanf(stella_icon[0], "%d %d %d %d", &w, &h, &ncols, &nbytes);
if((w != 32) || (h != 32) || (ncols > 255) || (nbytes > 1))
{
cerr << "ERROR: Couldn't load the icon.\n";
return;
}
for(uInt32 i = 0; i < ncols; i++)
{
unsigned char code;
char color[32];
uInt32 col;
sscanf(stella_icon[1 + i], "%c c %s", &code, color);
if(!strcmp(color, "None"))
col = 0x00000000;
else if(!strcmp(color, "black"))
col = 0xFF000000;
else if (color[0] == '#')
{
sscanf(color + 1, "%06x", &col);
col |= 0xFF000000;
}
else
{
cerr << "ERROR: Couldn't load the icon.\n";
return;
}
rgba[code] = col;
}
memset(mask, 0, sizeof(mask));
for(h = 0; h < 32; h++)
{
const char* line = stella_icon[1 + ncols + h];
for(w = 0; w < 32; w++)
{
icon[w + 32 * h] = rgba[(int)line[w]];
if(rgba[(int)line[w]] & 0xFF000000)
mask[h][w >> 3] |= 1 << (7 - (w & 0x07));
}
}
SDL_Surface *surface = SDL_CreateRGBSurfaceFrom(icon, 32, 32, 32,
32 * 4, 0xFF0000, 0x00FF00, 0x0000FF, 0xFF000000);
SDL_WM_SetIcon(surface, (unsigned char *) mask);
SDL_FreeSurface(surface);
#endif
}

View File

@ -1,163 +0,0 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-1999 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: FrameBufferSDL.hxx,v 1.11 2004-04-26 12:49:46 stephena Exp $
//============================================================================
#ifndef FRAMEBUFFER_SDL_HXX
#define FRAMEBUFFER_SDL_HXX
#include <SDL.h>
#include <SDL_syswm.h>
#include "FrameBuffer.hxx"
#include "Settings.hxx"
#include "bspf.hxx"
/**
This class is a base class for the SDL framebuffer and is derived from
the core FrameBuffer class.
It defines all common code shared between the software
and OpenGL video modes, as well as required methods defined in
the core FrameBuffer.
@author Stephen Anthony
@version $Id: FrameBufferSDL.hxx,v 1.11 2004-04-26 12:49:46 stephena Exp $
*/
class FrameBufferSDL : public FrameBuffer
{
public:
/**
Creates a new SDL framebuffer
*/
FrameBufferSDL();
/**
Destructor
*/
virtual ~FrameBufferSDL();
/**
Toggles between fullscreen and window mode. Grabmouse and hidecursor
activated when in fullscreen mode.
*/
void toggleFullscreen();
/**
This routine is called when the user wants to resize the window.
A '1' argument indicates that the window should increase in size, while '-1'
indicates that the windows should decrease in size. A '0' indicates that
the window should be sized according to the current properties.
Can't resize in fullscreen mode. Will only resize up to the maximum size
of the screen.
*/
void resize(int mode);
/**
Shows or hides the cursor based on the given boolean value.
*/
void showCursor(bool show);
/**
Grabs or ungrabs the mouse based on the given boolean value.
*/
void grabMouse(bool grab);
/**
Answers if the display is currently in fullscreen mode.
*/
bool fullScreen();
/**
Answers the current zoom level of the SDL
*/
uInt32 zoomLevel() { return theZoomLevel; }
/**
Calculate the maximum window size that the current screen can hold.
Only works in X11 for now. If not running under X11, always return 4.
*/
uInt32 maxWindowSizeForScreen();
/**
Set the title and icon for the main SDL window.
*/
void setWindowAttributes();
/**
Set up the palette for a screen of any depth > 8.
*/
void setupPalette();
//////////////////////////////////////////////////////////////////////
// The following methods are derived from FrameBuffer.hxx
//////////////////////////////////////////////////////////////////////
/**
This routine is called when the emulation has been paused.
@param status Toggle pause based on status
*/
void pauseEvent(bool status);
//////////////////////////////////////////////////////////////////////
// The following methods must be defined in child classes
//////////////////////////////////////////////////////////////////////
/**
This routine is called whenever the screen needs to be recreated.
It updates the global screen variable.
*/
virtual bool createScreen() = 0;
/**
This routine is called to map a given r,g,b triple to the screen palette.
@param r The red component of the color.
@param g The green component of the color.
@param b The blue component of the color.
*/
virtual Uint32 mapRGB(Uint8 r, Uint8 g, Uint8 b) = 0;
protected:
// The SDL video buffer
SDL_Surface* myScreen;
// SDL initialization flags
uInt32 mySDLFlags;
// SDL palette
Uint32 myPalette[256];
// Used to get window-manager specifics
SDL_SysWMinfo myWMInfo;
// Indicates if we are running under X11
bool x11Available;
// Indicates the current zoom level of the SDL screen
uInt32 theZoomLevel;
// Indicates the maximum zoom of the SDL screen
uInt32 theMaxZoomLevel;
// The aspect ratio of the window
float theAspectRatio;
// Indicates whether the emulation has paused
bool myPauseStatus;
};
#endif

View File

@ -1,440 +0,0 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-1999 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: FrameBufferSoft.cxx,v 1.7 2004-04-26 12:49:46 stephena Exp $
//============================================================================
#include <SDL.h>
#include <SDL_syswm.h>
#include <sstream>
#include "Console.hxx"
#include "FrameBuffer.hxx"
#include "FrameBufferSDL.hxx"
#include "FrameBufferSoft.hxx"
#include "MediaSrc.hxx"
#include "Settings.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FrameBufferSoft::FrameBufferSoft()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FrameBufferSoft::~FrameBufferSoft()
{
if(myRectList)
delete myRectList;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameBufferSoft::createScreen()
{
int w = myWidth * theZoomLevel;
int h = myHeight * theZoomLevel;
myScreen = SDL_SetVideoMode(w, h, 0, mySDLFlags);
if(myScreen == NULL)
{
cerr << "ERROR: Unable to open SDL window: " << SDL_GetError() << endl;
return false;
}
theRedrawEntireFrameIndicator = true;
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameBufferSoft::init()
{
// Get the desired width and height of the display
myWidth = myMediaSource->width() << 1;
myHeight = myMediaSource->height();
// Now create the software SDL screen
Uint32 initflags = SDL_INIT_VIDEO | SDL_INIT_TIMER;
if(SDL_Init(initflags) < 0)
return false;
// Check which system we are running under
x11Available = false;
#if UNIX && (!__APPLE__)
SDL_VERSION(&myWMInfo.version);
if(SDL_GetWMInfo(&myWMInfo) > 0)
if(myWMInfo.subsystem == SDL_SYSWM_X11)
x11Available = true;
#endif
// Get the maximum size of a window for THIS screen
theMaxZoomLevel = maxWindowSizeForScreen();
// Check to see if window size will fit in the screen
if((uInt32)myConsole->settings().getInt("zoom") > theMaxZoomLevel)
theZoomLevel = theMaxZoomLevel;
else
theZoomLevel = myConsole->settings().getInt("zoom");
mySDLFlags = SDL_SWSURFACE;
mySDLFlags |= myConsole->settings().getBool("fullscreen") ? SDL_FULLSCREEN : 0;
// Set up the rectangle list to be used in the dirty update
myRectList = new RectList();
if(!myRectList)
{
cerr << "ERROR: Unable to get memory for SDL rects" << endl;
return false;
}
// Set the window title and icon
setWindowAttributes();
// Create the screen
if(!createScreen())
return false;
setupPalette();
// Make sure that theUseFullScreenFlag sets up fullscreen mode correctly
if(myConsole->settings().getBool("fullscreen"))
{
grabMouse(true);
showCursor(false);
}
else
{
// Keep mouse in game window if grabmouse is selected
grabMouse(myConsole->settings().getBool("grabmouse"));
// Show or hide the cursor depending on the 'hidecursor' argument
showCursor(!myConsole->settings().getBool("hidecursor"));
}
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSoft::drawMediaSource()
{
uInt8* currentFrame = myMediaSource->currentFrameBuffer();
uInt8* previousFrame = myMediaSource->previousFrameBuffer();
uInt16 screenMultiple = (uInt16) theZoomLevel;
uInt32 width = myMediaSource->width();
uInt32 height = myMediaSource->height();
struct Rectangle
{
uInt8 color;
uInt16 x, y, width, height;
} rectangles[2][160];
// This array represents the rectangles that need displaying
// on the current scanline we're processing
Rectangle* currentRectangles = rectangles[0];
// This array represents the rectangles that are still active
// from the previous scanlines we have processed
Rectangle* activeRectangles = rectangles[1];
// Indicates the number of active rectangles
uInt16 activeCount = 0;
// This update procedure requires theWidth to be a multiple of four.
// This is validated when the properties are loaded.
for(uInt16 y = 0; y < height; ++y)
{
// Indicates the number of current rectangles
uInt16 currentCount = 0;
// Look at four pixels at a time to see if anything has changed
uInt32* current = (uInt32*)(currentFrame);
uInt32* previous = (uInt32*)(previousFrame);
for(uInt16 x = 0; x < width; x += 4, ++current, ++previous)
{
// Has something changed in this set of four pixels?
if((*current != *previous) || theRedrawEntireFrameIndicator)
{
uInt8* c = (uInt8*)current;
uInt8* p = (uInt8*)previous;
// Look at each of the bytes that make up the uInt32
for(uInt16 i = 0; i < 4; ++i, ++c, ++p)
{
// See if this pixel has changed
if((*c != *p) || theRedrawEntireFrameIndicator)
{
// Can we extend a rectangle or do we have to create a new one?
if((currentCount != 0) &&
(currentRectangles[currentCount - 1].color == *c) &&
((currentRectangles[currentCount - 1].x +
currentRectangles[currentCount - 1].width) == (x + i)))
{
currentRectangles[currentCount - 1].width += 1;
}
else
{
currentRectangles[currentCount].x = x + i;
currentRectangles[currentCount].y = y;
currentRectangles[currentCount].width = 1;
currentRectangles[currentCount].height = 1;
currentRectangles[currentCount].color = *c;
currentCount++;
}
}
}
}
}
// Merge the active and current rectangles flushing any that are of no use
uInt16 activeIndex = 0;
for(uInt16 t = 0; (t < currentCount) && (activeIndex < activeCount); ++t)
{
Rectangle& current = currentRectangles[t];
Rectangle& active = activeRectangles[activeIndex];
// Can we merge the current rectangle with an active one?
if((current.x == active.x) && (current.width == active.width) &&
(current.color == active.color))
{
current.y = active.y;
current.height = active.height + 1;
++activeIndex;
}
// Is it impossible for this active rectangle to be merged?
else if(current.x >= active.x)
{
// Flush the active rectangle
SDL_Rect temp;
temp.x = active.x * screenMultiple << 1;
temp.y = active.y * screenMultiple;
temp.w = active.width * screenMultiple << 1;
temp.h = active.height * screenMultiple;
myRectList->add(&temp);
SDL_FillRect(myScreen, &temp, myPalette[active.color]);
++activeIndex;
}
}
// Flush any remaining active rectangles
for(uInt16 s = activeIndex; s < activeCount; ++s)
{
Rectangle& active = activeRectangles[s];
SDL_Rect temp;
temp.x = active.x * screenMultiple << 1;
temp.y = active.y * screenMultiple;
temp.w = active.width * screenMultiple << 1;
temp.h = active.height * screenMultiple;
myRectList->add(&temp);
SDL_FillRect(myScreen, &temp, myPalette[active.color]);
}
// We can now make the current rectangles into the active rectangles
Rectangle* tmp = currentRectangles;
currentRectangles = activeRectangles;
activeRectangles = tmp;
activeCount = currentCount;
currentFrame += width;
previousFrame += width;
}
// Flush any rectangles that are still active
for(uInt16 t = 0; t < activeCount; ++t)
{
Rectangle& active = activeRectangles[t];
SDL_Rect temp;
temp.x = active.x * screenMultiple << 1;
temp.y = active.y * screenMultiple;
temp.w = active.width * screenMultiple << 1;
temp.h = active.height * screenMultiple;
myRectList->add(&temp);
SDL_FillRect(myScreen, &temp, myPalette[active.color]);
}
// The frame doesn't need to be completely redrawn anymore
theRedrawEntireFrameIndicator = false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSoft::preFrameUpdate()
{
// Start a new rectlist on each display update
myRectList->start();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSoft::postFrameUpdate()
{
// Now update all the rectangles at once
SDL_UpdateRects(myScreen, myRectList->numRects(), myRectList->rects());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSoft::drawBoundedBox(uInt32 x, uInt32 y, uInt32 w, uInt32 h)
{
SDL_Rect tmp;
// Scale all values to the current window size
x *= theZoomLevel;
y *= theZoomLevel;
w *= theZoomLevel;
h *= theZoomLevel;
// First draw the underlying box
tmp.x = x;
tmp.y = y;
tmp.w = w;
tmp.h = h;
myRectList->add(&tmp);
SDL_FillRect(myScreen, &tmp, myPalette[myBGColor]);
// Now draw the bounding sides
tmp.x = x;
tmp.y = y;
tmp.w = w;
tmp.h = theZoomLevel;
SDL_FillRect(myScreen, &tmp, myPalette[myFGColor]); // top
tmp.x = x;
tmp.y = y + h - theZoomLevel;
tmp.w = w;
tmp.h = theZoomLevel;
SDL_FillRect(myScreen, &tmp, myPalette[myFGColor]); // bottom
tmp.x = x;
tmp.y = y;
tmp.w = theZoomLevel;
tmp.h = h;
SDL_FillRect(myScreen, &tmp, myPalette[myFGColor]); // left
tmp.x = x + w - theZoomLevel;
tmp.y = y;
tmp.w = theZoomLevel;
tmp.h = h;
SDL_FillRect(myScreen, &tmp, myPalette[myFGColor]); // right
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSoft::drawText(uInt32 xorig, uInt32 yorig, const string& message)
{
SDL_Rect tmp;
uInt8 length = message.length();
for(uInt32 x = 0; x < length; x++)
{
for(uInt32 y = 0; y < 8; y++)
{
for(uInt32 z = 0; z < 8; z++)
{
char letter = message[x];
if((ourFontData[(letter << 3) + y] >> z) & 1)
{
tmp.x = ((x<<3) + z + xorig) * theZoomLevel;
tmp.y = (y + yorig) * theZoomLevel;
tmp.w = tmp.h = theZoomLevel;
SDL_FillRect(myScreen, &tmp, myPalette[myFGColor]);
}
}
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSoft::drawChar(uInt32 xorig, uInt32 yorig, uInt32 c)
{
if(c >= 256 )
return;
SDL_Rect tmp;
for(uInt32 y = 0; y < 8; y++)
{
for(uInt32 z = 0; z < 8; z++)
{
if((ourFontData[(c << 3) + y] >> z) & 1)
{
tmp.x = (z + xorig) * theZoomLevel;
tmp.y = (y + yorig) * theZoomLevel;
tmp.w = tmp.h = theZoomLevel;
SDL_FillRect(myScreen, &tmp, myPalette[myFGColor]);
}
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RectList::RectList(Uint32 size)
{
currentSize = size;
currentRect = 0;
rectArray = new SDL_Rect[currentSize];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RectList::~RectList()
{
delete[] rectArray;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RectList::add(SDL_Rect* newRect)
{
if(currentRect >= currentSize)
{
currentSize = currentSize * 2;
SDL_Rect *temp = new SDL_Rect[currentSize];
for(Uint32 i = 0; i < currentRect; ++i)
temp[i] = rectArray[i];
delete[] rectArray;
rectArray = temp;
}
rectArray[currentRect].x = newRect->x;
rectArray[currentRect].y = newRect->y;
rectArray[currentRect].w = newRect->w;
rectArray[currentRect].h = newRect->h;
++currentRect;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SDL_Rect* RectList::rects()
{
return rectArray;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Uint32 RectList::numRects()
{
return currentRect;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RectList::start()
{
currentRect = 0;
}

View File

@ -1,153 +0,0 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-1999 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: FrameBufferSoft.hxx,v 1.3 2004-04-15 22:52:43 stephena Exp $
//============================================================================
#ifndef FRAMEBUFFER_SOFT_HXX
#define FRAMEBUFFER_SOFT_HXX
#include <SDL.h>
#include <SDL_syswm.h>
#include "FrameBuffer.hxx"
#include "FrameBufferSDL.hxx"
#include "bspf.hxx"
class Console;
class MediaSource;
class RectList;
/**
This class implements an SDL software framebuffer.
@author Stephen Anthony
@version $Id: FrameBufferSoft.hxx,v 1.3 2004-04-15 22:52:43 stephena Exp $
*/
class FrameBufferSoft : public FrameBufferSDL
{
public:
/**
Creates a new SDL software framebuffer
*/
FrameBufferSoft();
/**
Destructor
*/
virtual ~FrameBufferSoft();
//////////////////////////////////////////////////////////////////////
// The following methods are derived from FrameBufferSDL.hxx
//////////////////////////////////////////////////////////////////////
/**
This routine is called whenever the screen needs to be recreated.
It updates the global screen variable.
*/
virtual bool createScreen();
/**
This routine is called to map a given r,g,b triple to the screen palette.
@param r The red component of the color.
@param g The green component of the color.
@param b The blue component of the color.
*/
virtual Uint32 mapRGB(Uint8 r, Uint8 g, Uint8 b)
{ return SDL_MapRGB(myScreen->format, r, g, b); }
//////////////////////////////////////////////////////////////////////
// The following methods are derived from FrameBuffer.hxx
//////////////////////////////////////////////////////////////////////
/**
This routine should be called once the console is created to setup
the video system for us to use. Return false if any operation fails,
otherwise return true.
*/
virtual bool init();
/**
This routine should be called anytime the MediaSource needs to be redrawn
to the screen.
*/
virtual void drawMediaSource();
/**
This routine should be called to draw a rectangular box with sides
at the specified coordinates.
@param x The x coordinate
@param y The y coordinate
@param w The width of the box
@param h The height of the box
*/
virtual void drawBoundedBox(uInt32 x, uInt32 y, uInt32 w, uInt32 h);
/**
This routine should be called to draw text at the specified coordinates.
@param x The x coordinate
@param y The y coordinate
@param message The message text
*/
virtual void drawText(uInt32 x, uInt32 y, const string& message);
/**
This routine should be called to draw character 'c' at the specified coordinates.
@param x The x coordinate
@param y The y coordinate
@param c The character to draw
*/
virtual void drawChar(uInt32 x, uInt32 y, uInt32 c);
/**
This routine is called before any drawing is done (per-frame).
*/
virtual void preFrameUpdate();
/**
This routine is called after any drawing is done (per-frame).
*/
virtual void postFrameUpdate();
private:
// Used in the dirty update of the SDL surface
RectList* myRectList;
};
/**
*/
class RectList
{
public:
RectList(Uint32 size = 512);
~RectList();
void add(SDL_Rect* rect);
SDL_Rect* rects();
Uint32 numRects();
void start();
private:
Uint32 currentSize, currentRect;
SDL_Rect* rectArray;
};
#endif

View File

@ -1,182 +0,0 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-1999 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: SettingsUNIX.cxx,v 1.9 2004-04-27 00:50:52 stephena Exp $
//============================================================================
#include <cstdlib>
#include <sstream>
#include <fstream>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "bspf.hxx"
#include "Console.hxx"
#include "EventHandler.hxx"
#include "StellaEvent.hxx"
#include "Settings.hxx"
#include "SettingsUNIX.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SettingsUNIX::SettingsUNIX()
{
// First set variables that the parent class needs
myBaseDir = getenv("HOME");
string stelladir = myBaseDir + "/.stella";
if(access(stelladir.c_str(), R_OK|W_OK|X_OK) != 0 )
mkdir(stelladir.c_str(), 0777);
myStateDir = stelladir + "/state/";
if(access(myStateDir.c_str(), R_OK|W_OK|X_OK) != 0 )
mkdir(myStateDir.c_str(), 0777);
myUserPropertiesFile = stelladir + "/stella.pro";
mySystemPropertiesFile = "/etc/stella.pro";
myUserConfigFile = stelladir + "/stellarc";
mySystemConfigFile = "/etc/stellarc";
// Set up the names of the input and output config files
mySettingsOutputFilename = myUserConfigFile;
if(access(myUserConfigFile.c_str(), R_OK) == 0)
mySettingsInputFilename = myUserConfigFile;
else
mySettingsInputFilename = mySystemConfigFile;
mySnapshotFile = "";
myStateFile = "";
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SettingsUNIX::~SettingsUNIX()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SettingsUNIX::usage(string& message)
{
cout << endl
<< message << endl
<< endl
<< "Valid options are:" << endl
<< endl
<< " -video <type> Type is one of the following:\n"
<< " soft SDL software mode\n"
#ifdef DISPLAY_OPENGL
<< " gl SDL OpenGL mode\n"
<< endl
<< " -gl_filter <type> Type is one of the following:\n"
<< " nearest Normal scaling (GL_NEAREST)\n"
<< " linear Blurred scaling (GL_LINEAR)\n"
<< " -gl_aspect <number> Scale the width by the given amount\n"
<< endl
#endif
<< " -sound <0|1> Enable sound generation\n"
<< " -fragsize <number> The size of sound fragments (should be a power of two)\n"
<< " -bufsize <number> The size of the sound buffer\n"
<< " -framerate <number> Display the given number of frames per second\n"
<< " -zoom <size> Makes window be 'size' times normal\n"
<< " -fullscreen <0|1> Play the game in fullscreen mode\n"
<< " -grabmouse <0|1> Keeps the mouse in the game window\n"
<< " -hidecursor <0|1> Hides the mouse cursor in the game window\n"
<< " -volume <number> Set the volume (0 - 100)\n"
#ifdef JOYSTICK_SUPPORT
<< " -paddle <0|1|2|3|real> Indicates which paddle the mouse should emulate\n"
<< " or that real Atari 2600 paddles are being used\n"
#else
<< " -paddle <0|1|2|3> Indicates which paddle the mouse should emulate\n"
#endif
<< " -altpro <props file> Use the given properties file instead of stella.pro\n"
<< " -showinfo <0|1> Shows some game info\n"
<< " -accurate <0|1> Accurate game timing (uses more CPU)\n"
#ifdef SNAPSHOT_SUPPORT
<< " -ssdir <path> The directory to save snapshot files to\n"
<< " -ssname <name> How to name the snapshot (romname or md5sum)\n"
<< " -sssingle <0|1> Generate single snapshot instead of many\n"
#endif
<< endl
#ifdef DEVELOPER_SUPPORT
<< " DEVELOPER options (see Stella manual for details)\n"
<< " -Dformat Sets \"Display.Format\"\n"
<< " -Dxstart Sets \"Display.XStart\"\n"
<< " -Dwidth Sets \"Display.Width\"\n"
<< " -Dystart Sets \"Display.YStart\"\n"
<< " -Dheight Sets \"Display.Height\"\n"
<< " -mergeprops <0|1> Merge changed properties into properties file,\n"
<< " or save into a separate file\n"
#endif
<< endl;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string SettingsUNIX::stateFilename(uInt32 state)
{
if(!myConsole)
return "";
ostringstream buf;
buf << myStateDir << myConsole->properties().get("Cartridge.MD5")
<< ".st" << state;
myStateFile = buf.str();
return myStateFile;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string SettingsUNIX::snapshotFilename()
{
if(!myConsole)
return "";
string filename;
string path = getString("ssdir");
string theSnapshotName = getString("ssname");
if(theSnapshotName == "romname")
path = path + "/" + myConsole->properties().get("Cartridge.Name");
else if(theSnapshotName == "md5sum")
path = path + "/" + myConsole->properties().get("Cartridge.MD5");
// Replace all spaces in name with underscores
replace(path.begin(), path.end(), ' ', '_');
// Check whether we want multiple snapshots created
if(!getBool("sssingle"))
{
// Determine if the file already exists, checking each successive filename
// until one doesn't exist
filename = path + ".png";
if(access(filename.c_str(), F_OK) == 0 )
{
ostringstream buf;
for(uInt32 i = 1; ;++i)
{
buf.str("");
buf << path << "_" << i << ".png";
if(access(buf.str().c_str(), F_OK) == -1 )
break;
}
filename = buf.str();
}
}
else
filename = path + ".png";
mySnapshotFile = filename;
return mySnapshotFile;
}

View File

@ -1,70 +0,0 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-1999 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: SettingsUNIX.hxx,v 1.1 2003-10-26 19:40:39 stephena Exp $
//============================================================================
#ifndef SETTINGS_UNIX_HXX
#define SETTINGS_UNIX_HXX
#include "bspf.hxx"
class Console;
/**
This class defines UNIX-like OS's (Linux) system specific settings.
@author Stephen Anthony
@version $Id: SettingsUNIX.hxx,v 1.1 2003-10-26 19:40:39 stephena Exp $
*/
class SettingsUNIX : public Settings
{
public:
/**
Create a new UNIX settings object
*/
SettingsUNIX();
/**
Destructor
*/
virtual ~SettingsUNIX();
public:
/**
This method should be called to get the filename of a state file
given the state number.
@return String representing the full path of the state filename.
*/
virtual string stateFilename(uInt32 state);
/**
This method should be called to get the filename of a snapshot.
@return String representing the full path of the snapshot filename.
*/
virtual string snapshotFilename();
/**
Display the commandline settings for this UNIX version of Stella.
@param message A short message about this version of Stella
*/
virtual void usage(string& message);
};
#endif

View File

@ -1,188 +0,0 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-1999 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: SettingsWin32.cxx,v 1.4 2004-04-27 00:50:52 stephena Exp $
//============================================================================
#include <cstdlib>
#include <sstream>
#include <fstream>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "bspf.hxx"
#include "Console.hxx"
#include "EventHandler.hxx"
#include "StellaEvent.hxx"
#include "Settings.hxx"
#include "SettingsWin32.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SettingsWin32::SettingsWin32()
{
// First set variables that the parent class needs
myBaseDir = ".\\";
string stelladir = myBaseDir;
// if(access(stelladir.c_str(), R_OK|W_OK|X_OK) != 0 )
// mkdir(stelladir.c_str(), 0777);
myStateDir = stelladir + "state\\";
// if(access(myStateDir.c_str(), R_OK|W_OK|X_OK) != 0 )
// mkdir(myStateDir.c_str(), 0777);
myUserPropertiesFile = stelladir + "stella.pro";
mySystemPropertiesFile = stelladir + "stella.pro";
myUserConfigFile = stelladir + "stellarc";
mySystemConfigFile = stelladir + "stellarc";
// Set up the names of the input and output config files
mySettingsOutputFilename = myUserConfigFile;
// if(access(myUserConfigFile.c_str(), R_OK) == 0)
mySettingsInputFilename = myUserConfigFile;
// else
// mySettingsInputFilename = mySystemConfigFile;
mySnapshotFile = "";
myStateFile = "";
// Now create Win32 specific settings
set("accurate", "false"); // Don't change this, or the sound will skip
#ifdef SNAPSHOT_SUPPORT
set("ssdir", ".\\");
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SettingsWin32::~SettingsWin32()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SettingsWin32::usage(string& message)
{
cout << endl
<< message << endl
<< endl
<< "Valid options are:" << endl
<< endl
<< " -video <type> Type is one of the following:\n"
<< " soft SDL software mode\n"
#ifdef DISPLAY_OPENGL
<< " gl SDL OpenGL mode\n"
<< endl
<< " -gl_filter <type> Type is one of the following:\n"
<< " nearest Normal scaling (GL_NEAREST)\n"
<< " linear Blurred scaling (GL_LINEAR)\n"
<< " -gl_aspect <number> Scale the width by the given amount\n"
<< endl
#endif
<< " -sound <0|1> Enable sound generation\n"
<< " -fragsize <number> The size of sound fragments (should be a power of two)\n"
<< " -bufsize <number> The size of the sound buffer\n"
<< " -framerate <number> Display the given number of frames per second\n"
<< " -zoom <size> Makes window be 'size' times normal\n"
<< " -fullscreen <0|1> Play the game in fullscreen mode\n"
<< " -grabmouse <0|1> Keeps the mouse in the game window\n"
<< " -hidecursor <0|1> Hides the mouse cursor in the game window\n"
<< " -volume <number> Set the volume (0 - 100)\n"
#ifdef JOYSTICK_SUPPORT
<< " -paddle <0|1|2|3|real> Indicates which paddle the mouse should emulate\n"
<< " or that real Atari 2600 paddles are being used\n"
#else
<< " -paddle <0|1|2|3> Indicates which paddle the mouse should emulate\n"
#endif
<< " -altpro <props file> Use the given properties file instead of stella.pro\n"
<< " -showinfo <0|1> Shows some game info\n"
<< " -accurate <0|1> Accurate game timing (uses more CPU)\n"
#ifdef SNAPSHOT_SUPPORT
<< " -ssdir <path> The directory to save snapshot files to\n"
<< " -ssname <name> How to name the snapshot (romname or md5sum)\n"
<< " -sssingle <0|1> Generate single snapshot instead of many\n"
#endif
<< endl
#ifdef DEVELOPER_SUPPORT
<< " DEVELOPER options (see Stella manual for details)\n"
<< " -Dformat Sets \"Display.Format\"\n"
<< " -Dxstart Sets \"Display.XStart\"\n"
<< " -Dwidth Sets \"Display.Width\"\n"
<< " -Dystart Sets \"Display.YStart\"\n"
<< " -Dheight Sets \"Display.Height\"\n"
<< " -mergeprops <0|1> Merge changed properties into properties file,\n"
<< " or save into a separate file\n"
#endif
<< endl;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string SettingsWin32::stateFilename(uInt32 state)
{
if(!myConsole)
return "";
ostringstream buf;
buf << myStateDir << myConsole->properties().get("Cartridge.MD5")
<< ".st" << state;
myStateFile = buf.str();
return myStateFile;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string SettingsWin32::snapshotFilename()
{
if(!myConsole)
return "";
string filename;
string path = getString("ssdir");
string theSnapshotName = getString("ssname");
if(theSnapshotName == "romname")
path = path + "\\" + myConsole->properties().get("Cartridge.Name");
else if(theSnapshotName == "md5sum")
path = path + "\\" + myConsole->properties().get("Cartridge.MD5");
// Replace all spaces in name with underscores
replace(path.begin(), path.end(), ' ', '_');
// Check whether we want multiple snapshots created
if(!getBool("sssingle"))
{
// Determine if the file already exists, checking each successive filename
// until one doesn't exist
filename = path + ".png";
if(access(filename.c_str(), F_OK) == 0 )
{
ostringstream buf;
for(uInt32 i = 1; ;++i)
{
buf.str("");
buf << path << "_" << i << ".png";
if(access(buf.str().c_str(), F_OK) == -1 )
break;
}
filename = buf.str();
}
}
else
filename = path + ".png";
mySnapshotFile = filename;
return mySnapshotFile;
}

View File

@ -1,70 +0,0 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-1999 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: SettingsWin32.hxx,v 1.1 2003-12-04 22:22:53 stephena Exp $
//============================================================================
#ifndef SETTINGS_WIN32_HXX
#define SETTINGS_WIN32_HXX
#include "bspf.hxx"
class Console;
/**
This class defines Windows system specific settings.
@author Stephen Anthony
@version $Id: SettingsWin32.hxx,v 1.1 2003-12-04 22:22:53 stephena Exp $
*/
class SettingsWin32 : public Settings
{
public:
/**
Create a new UNIX settings object
*/
SettingsWin32();
/**
Destructor
*/
virtual ~SettingsWin32();
public:
/**
This method should be called to get the filename of a state file
given the state number.
@return String representing the full path of the state filename.
*/
virtual string stateFilename(uInt32 state);
/**
This method should be called to get the filename of a snapshot.
@return String representing the full path of the snapshot filename.
*/
virtual string snapshotFilename();
/**
Display the commandline settings for this UNIX version of Stella.
@param message A short message about this version of Stella
*/
virtual void usage(string& message);
};
#endif

View File

@ -1,992 +0,0 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2002 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: mainSDL.cxx,v 1.76 2004-05-13 12:38:17 stephena Exp $
//============================================================================
#include <fstream>
#include <iostream>
#include <string>
#include <algorithm>
#include <cstdlib>
#ifdef HAVE_GETTIMEOFDAY
#include <time.h>
#include <sys/time.h>
#endif
#include <SDL.h>
#include "bspf.hxx"
#include "Console.hxx"
#include "Event.hxx"
#include "StellaEvent.hxx"
#include "EventHandler.hxx"
#include "FrameBuffer.hxx"
#include "FrameBufferSDL.hxx"
#include "FrameBufferSoft.hxx"
#include "PropsSet.hxx"
#include "Sound.hxx"
#include "SoundSDL.hxx"
#include "Settings.hxx"
#ifdef DISPLAY_OPENGL
#include "FrameBufferGL.hxx"
// Indicates whether to use OpenGL mode
static bool theUseOpenGLFlag;
#endif
#if defined(UNIX)
#include "SettingsUNIX.hxx"
#elif defined(WIN32)
#include "SettingsWin32.hxx"
#else
#error Unsupported platform!
#endif
static void cleanup();
static bool setupJoystick();
static void handleEvents();
static uInt32 getTicks();
static bool setupProperties(PropertiesSet& set);
#ifdef JOYSTICK_SUPPORT
// static uInt32 thePaddleNumber;
// Lookup table for joystick numbers and events
StellaEvent::JoyStick joyList[StellaEvent::LastJSTICK] = {
StellaEvent::JSTICK_0, StellaEvent::JSTICK_1, StellaEvent::JSTICK_2,
StellaEvent::JSTICK_3, StellaEvent::JSTICK_5, StellaEvent::JSTICK_5
};
StellaEvent::JoyCode joyButtonList[StellaEvent::LastJCODE] = {
StellaEvent::JBUTTON_0, StellaEvent::JBUTTON_1, StellaEvent::JBUTTON_2,
StellaEvent::JBUTTON_3, StellaEvent::JBUTTON_4, StellaEvent::JBUTTON_5,
StellaEvent::JBUTTON_6, StellaEvent::JBUTTON_7, StellaEvent::JBUTTON_8,
StellaEvent::JBUTTON_9, StellaEvent::JBUTTON_10, StellaEvent::JBUTTON_11,
StellaEvent::JBUTTON_12, StellaEvent::JBUTTON_13, StellaEvent::JBUTTON_14,
StellaEvent::JBUTTON_15, StellaEvent::JBUTTON_16, StellaEvent::JBUTTON_17,
StellaEvent::JBUTTON_18, StellaEvent::JBUTTON_19
};
enum JoyType { JT_NONE, JT_REGULAR, JT_STELLADAPTOR_1, JT_STELLADAPTOR_2 };
struct Stella_Joystick
{
SDL_Joystick* stick;
JoyType type;
};
static Stella_Joystick theJoysticks[StellaEvent::LastJSTICK];
// Static lookup tables for Stelladaptor axis support
static Event::Type SA_Axis[2][2][3] = {
Event::JoystickZeroLeft, Event::JoystickZeroRight, Event::PaddleZeroResistance,
Event::JoystickZeroUp, Event::JoystickZeroDown, Event::PaddleOneResistance,
Event::JoystickOneLeft, Event::JoystickOneRight, Event::PaddleTwoResistance,
Event::JoystickOneUp, Event::JoystickOneDown, Event::PaddleThreeResistance
};
#endif
// Pointer to the console object or the null pointer
static Console* theConsole = (Console*) NULL;
// Pointer to the display object or the null pointer
static FrameBufferSDL* theDisplay = (FrameBufferSDL*) NULL;
// Pointer to the sound object or the null pointer
static Sound* theSound = (Sound*) NULL;
// Pointer to the settings object or the null pointer
static Settings* theSettings = (Settings*) NULL;
// Indicates if the mouse should be grabbed
static bool theGrabMouseIndicator = false;
// Indicates if the mouse cursor should be hidden
static bool theHideCursorIndicator = false;
// Indicates the current paddle mode
static Int32 thePaddleMode;
// Indicates relative mouse position horizontally
static Int32 mouseX = 0;
// Indicates whether to show information during program execution
static bool theShowInfoFlag;
struct Switches
{
SDLKey scanCode;
StellaEvent::KeyCode keyCode;
};
// Place the most used keys first to speed up access
// Todo - initialize this array in the same order as the SDLK
// keys are defined, so it can be a constant-time LUT
static Switches keyList[] = {
{ SDLK_F1, StellaEvent::KCODE_F1 },
{ SDLK_F2, StellaEvent::KCODE_F2 },
{ SDLK_F3, StellaEvent::KCODE_F3 },
{ SDLK_F4, StellaEvent::KCODE_F4 },
{ SDLK_F5, StellaEvent::KCODE_F5 },
{ SDLK_F6, StellaEvent::KCODE_F6 },
{ SDLK_F7, StellaEvent::KCODE_F7 },
{ SDLK_F8, StellaEvent::KCODE_F8 },
{ SDLK_F9, StellaEvent::KCODE_F9 },
{ SDLK_F10, StellaEvent::KCODE_F10 },
{ SDLK_F11, StellaEvent::KCODE_F11 },
{ SDLK_F12, StellaEvent::KCODE_F12 },
{ SDLK_F13, StellaEvent::KCODE_F13 },
{ SDLK_F14, StellaEvent::KCODE_F14 },
{ SDLK_F15, StellaEvent::KCODE_F15 },
{ SDLK_UP, StellaEvent::KCODE_UP },
{ SDLK_DOWN, StellaEvent::KCODE_DOWN },
{ SDLK_LEFT, StellaEvent::KCODE_LEFT },
{ SDLK_RIGHT, StellaEvent::KCODE_RIGHT },
{ SDLK_SPACE, StellaEvent::KCODE_SPACE },
{ SDLK_LCTRL, StellaEvent::KCODE_LCTRL },
{ SDLK_RCTRL, StellaEvent::KCODE_RCTRL },
{ SDLK_LALT, StellaEvent::KCODE_LALT },
{ SDLK_RALT, StellaEvent::KCODE_RALT },
{ SDLK_LSUPER, StellaEvent::KCODE_LWIN },
{ SDLK_RSUPER, StellaEvent::KCODE_RWIN },
{ SDLK_MENU, StellaEvent::KCODE_MENU },
{ SDLK_a, StellaEvent::KCODE_a },
{ SDLK_b, StellaEvent::KCODE_b },
{ SDLK_c, StellaEvent::KCODE_c },
{ SDLK_d, StellaEvent::KCODE_d },
{ SDLK_e, StellaEvent::KCODE_e },
{ SDLK_f, StellaEvent::KCODE_f },
{ SDLK_g, StellaEvent::KCODE_g },
{ SDLK_h, StellaEvent::KCODE_h },
{ SDLK_i, StellaEvent::KCODE_i },
{ SDLK_j, StellaEvent::KCODE_j },
{ SDLK_k, StellaEvent::KCODE_k },
{ SDLK_l, StellaEvent::KCODE_l },
{ SDLK_m, StellaEvent::KCODE_m },
{ SDLK_n, StellaEvent::KCODE_n },
{ SDLK_o, StellaEvent::KCODE_o },
{ SDLK_p, StellaEvent::KCODE_p },
{ SDLK_q, StellaEvent::KCODE_q },
{ SDLK_r, StellaEvent::KCODE_r },
{ SDLK_s, StellaEvent::KCODE_s },
{ SDLK_t, StellaEvent::KCODE_t },
{ SDLK_u, StellaEvent::KCODE_u },
{ SDLK_v, StellaEvent::KCODE_v },
{ SDLK_w, StellaEvent::KCODE_w },
{ SDLK_x, StellaEvent::KCODE_x },
{ SDLK_y, StellaEvent::KCODE_y },
{ SDLK_z, StellaEvent::KCODE_z },
{ SDLK_0, StellaEvent::KCODE_0 },
{ SDLK_1, StellaEvent::KCODE_1 },
{ SDLK_2, StellaEvent::KCODE_2 },
{ SDLK_3, StellaEvent::KCODE_3 },
{ SDLK_4, StellaEvent::KCODE_4 },
{ SDLK_5, StellaEvent::KCODE_5 },
{ SDLK_6, StellaEvent::KCODE_6 },
{ SDLK_7, StellaEvent::KCODE_7 },
{ SDLK_8, StellaEvent::KCODE_8 },
{ SDLK_9, StellaEvent::KCODE_9 },
{ SDLK_KP0, StellaEvent::KCODE_KP0 },
{ SDLK_KP1, StellaEvent::KCODE_KP1 },
{ SDLK_KP2, StellaEvent::KCODE_KP2 },
{ SDLK_KP3, StellaEvent::KCODE_KP3 },
{ SDLK_KP4, StellaEvent::KCODE_KP4 },
{ SDLK_KP5, StellaEvent::KCODE_KP5 },
{ SDLK_KP6, StellaEvent::KCODE_KP6 },
{ SDLK_KP7, StellaEvent::KCODE_KP7 },
{ SDLK_KP8, StellaEvent::KCODE_KP8 },
{ SDLK_KP9, StellaEvent::KCODE_KP9 },
{ SDLK_KP_PERIOD, StellaEvent::KCODE_KP_PERIOD },
{ SDLK_KP_DIVIDE, StellaEvent::KCODE_KP_DIVIDE },
{ SDLK_KP_MULTIPLY, StellaEvent::KCODE_KP_MULTIPLY},
{ SDLK_KP_MINUS, StellaEvent::KCODE_KP_MINUS },
{ SDLK_KP_PLUS, StellaEvent::KCODE_KP_PLUS },
{ SDLK_KP_ENTER, StellaEvent::KCODE_KP_ENTER },
{ SDLK_KP_EQUALS, StellaEvent::KCODE_KP_EQUALS },
{ SDLK_BACKSPACE, StellaEvent::KCODE_BACKSPACE },
{ SDLK_TAB, StellaEvent::KCODE_TAB },
{ SDLK_CLEAR, StellaEvent::KCODE_CLEAR },
{ SDLK_RETURN, StellaEvent::KCODE_RETURN },
{ SDLK_ESCAPE, StellaEvent::KCODE_ESCAPE },
{ SDLK_COMMA, StellaEvent::KCODE_COMMA },
{ SDLK_MINUS, StellaEvent::KCODE_MINUS },
{ SDLK_PERIOD, StellaEvent::KCODE_PERIOD },
{ SDLK_SLASH, StellaEvent::KCODE_SLASH },
{ SDLK_BACKSLASH, StellaEvent::KCODE_BACKSLASH },
{ SDLK_SEMICOLON, StellaEvent::KCODE_SEMICOLON },
{ SDLK_EQUALS, StellaEvent::KCODE_EQUALS },
{ SDLK_QUOTE, StellaEvent::KCODE_QUOTE },
{ SDLK_BACKQUOTE, StellaEvent::KCODE_BACKQUOTE },
{ SDLK_LEFTBRACKET, StellaEvent::KCODE_LEFTBRACKET},
{ SDLK_RIGHTBRACKET,StellaEvent::KCODE_RIGHTBRACKET},
{ SDLK_PRINT, StellaEvent::KCODE_PRTSCREEN },
{ SDLK_MODE, StellaEvent::KCODE_SCRLOCK },
{ SDLK_PAUSE, StellaEvent::KCODE_PAUSE },
{ SDLK_INSERT, StellaEvent::KCODE_INSERT },
{ SDLK_HOME, StellaEvent::KCODE_HOME },
{ SDLK_PAGEUP, StellaEvent::KCODE_PAGEUP },
{ SDLK_DELETE, StellaEvent::KCODE_DELETE },
{ SDLK_END, StellaEvent::KCODE_END },
{ SDLK_PAGEDOWN, StellaEvent::KCODE_PAGEDOWN }
};
/**
Returns number of ticks in microseconds
*/
#ifdef HAVE_GETTIMEOFDAY
inline uInt32 getTicks()
{
timeval now;
gettimeofday(&now, 0);
return (uInt32) (now.tv_sec * 1000000 + now.tv_usec);
}
#else
inline uInt32 getTicks()
{
return (uInt32) SDL_GetTicks() * 1000;
}
#endif
/**
This routine should be called once setupDisplay is called
to create the joystick stuff.
*/
bool setupJoystick()
{
#ifdef JOYSTICK_SUPPORT
// Keep track of how many Stelladaptors we've found
uInt8 saCount = 0;
// First clear the joystick array
for(uInt32 i = 0; i < StellaEvent::LastJSTICK; i++)
{
theJoysticks[i].stick = (SDL_Joystick*) NULL;
theJoysticks[i].type = JT_NONE;
}
// Initialize the joystick subsystem
if((SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1) || (SDL_NumJoysticks() <= 0))
{
if(theShowInfoFlag)
cout << "No joysticks present, use the keyboard.\n";
return true;
}
// Try to open 4 regular joysticks and 2 Stelladaptor devices
uInt32 limit = SDL_NumJoysticks() <= StellaEvent::LastJSTICK ?
SDL_NumJoysticks() : StellaEvent::LastJSTICK;
for(uInt32 i = 0; i < limit; i++)
{
string name = SDL_JoystickName(i);
theJoysticks[i].stick = SDL_JoystickOpen(i);
// Skip if we couldn't open it for any reason
if(theJoysticks[i].stick == NULL)
continue;
// Figure out what type of joystick this is
if(name.find("Stelladaptor", 0) != string::npos)
{
saCount++;
if(saCount > 2) // Ignore more than 2 Stelladaptors
{
theJoysticks[i].type = JT_NONE;
continue;
}
else if(saCount == 1)
{
name = "Left Stelladaptor (Left joystick, Paddles 0 and 1, Left driving controller)";
theJoysticks[i].type = JT_STELLADAPTOR_1;
}
else if(saCount == 2)
{
name = "Right Stelladaptor (Right joystick, Paddles 2 and 3, Right driving controller)";
theJoysticks[i].type = JT_STELLADAPTOR_2;
}
if(theShowInfoFlag)
cout << "Joystick " << i << ": " << name << endl;
}
else
{
theJoysticks[i].type = JT_REGULAR;
if(theShowInfoFlag)
{
cout << "Joystick " << i << ": " << SDL_JoystickName(i)
<< " with " << SDL_JoystickNumButtons(theJoysticks[i].stick)
<< " buttons.\n";
}
}
}
#endif
return true;
}
/**
This routine should be called regularly to handle events
*/
void handleEvents()
{
SDL_Event event;
Uint8 type;
SDLKey key;
SDLMod mod;
// Check for an event
while(SDL_PollEvent(&event))
{
// keyboard events
if(event.type == SDL_KEYDOWN)
{
key = event.key.keysym.sym;
mod = event.key.keysym.mod;
type = event.type;
// An attempt to speed up event processing
// All SDL-specific event actions are accessed by either
// Control or Alt keys. So we quickly check for those.
if(mod & KMOD_ALT)
{
if(key == SDLK_EQUALS)
theDisplay->resize(1);
else if(key == SDLK_MINUS)
theDisplay->resize(-1);
else if(key == SDLK_RETURN)
{
theDisplay->toggleFullscreen();
}
#ifdef DISPLAY_OPENGL
else if(key == SDLK_f && theUseOpenGLFlag)
((FrameBufferGL*)theDisplay)->toggleFilter();
#endif
#ifdef DEVELOPER_SUPPORT
else if(key == SDLK_END) // Alt-End increases XStart
{
theConsole->changeXStart(1);
theDisplay->resize(0);
}
else if(key == SDLK_HOME) // Alt-Home decreases XStart
{
theConsole->changeXStart(0);
theDisplay->resize(0);
}
else if(key == SDLK_PAGEUP) // Alt-PageUp increases YStart
{
theConsole->changeYStart(1);
theDisplay->resize(0);
}
else if(key == SDLK_PAGEDOWN) // Alt-PageDown decreases YStart
{
theConsole->changeYStart(0);
theDisplay->resize(0);
}
#endif
}
else if(mod & KMOD_CTRL)
{
if(key == SDLK_g)
{
// don't change grabmouse in fullscreen mode
if(!theDisplay->fullScreen())
{
theGrabMouseIndicator = !theGrabMouseIndicator;
theSettings->setBool("grabmouse", theGrabMouseIndicator);
theDisplay->grabMouse(theGrabMouseIndicator);
}
}
else if(key == SDLK_h)
{
// don't change hidecursor in fullscreen mode
if(!theDisplay->fullScreen())
{
theHideCursorIndicator = !theHideCursorIndicator;
theSettings->setBool("hidecursor", theHideCursorIndicator);
theDisplay->showCursor(!theHideCursorIndicator);
}
}
#ifdef DEVELOPER_SUPPORT
else if(key == SDLK_f) // Ctrl-f toggles NTSC/PAL mode
{
theConsole->toggleFormat();
theDisplay->setupPalette();
}
else if(key == SDLK_p) // Ctrl-p toggles different palettes
{
theConsole->togglePalette();
theDisplay->setupPalette();
}
else if(key == SDLK_END) // Ctrl-End increases Width
{
theConsole->changeWidth(1);
theDisplay->resize(0);
}
else if(key == SDLK_HOME) // Ctrl-Home decreases Width
{
theConsole->changeWidth(0);
theDisplay->resize(0);
}
else if(key == SDLK_PAGEUP) // Ctrl-PageUp increases Height
{
theConsole->changeHeight(1);
theDisplay->resize(0);
}
else if(key == SDLK_PAGEDOWN) // Ctrl-PageDown decreases Height
{
theConsole->changeHeight(0);
theDisplay->resize(0);
}
else if(key == SDLK_s) // Ctrl-s saves properties to a file
{
if(theConsole->settings().getBool("mergeprops")) // Attempt to merge with propertiesSet
{
theConsole->saveProperties(theSettings->userPropertiesFilename(), true);
}
else // Save to file in home directory
{
string newPropertiesFile = theConsole->settings().baseDir() + "/" + \
theConsole->properties().get("Cartridge.Name") + ".pro";
replace(newPropertiesFile.begin(), newPropertiesFile.end(), ' ', '_');
theConsole->saveProperties(newPropertiesFile);
}
}
#endif
}
else // check all the other keys
{
for(unsigned int i = 0; i < sizeof(keyList) / sizeof(Switches); ++i)
{
if(keyList[i].scanCode == key)
theConsole->eventHandler().sendKeyEvent(keyList[i].keyCode, 1);
}
}
}
else if(event.type == SDL_KEYUP)
{
key = event.key.keysym.sym;
type = event.type;
for(unsigned int i = 0; i < sizeof(keyList) / sizeof(Switches); ++i)
{
if(keyList[i].scanCode == key)
theConsole->eventHandler().sendKeyEvent(keyList[i].keyCode, 0);
}
}
else if(event.type == SDL_MOUSEMOTION)
{
Int32 resistance;
uInt32 zoom = theDisplay->zoomLevel();
Int32 width = theDisplay->width() * zoom;
Event::Type type = Event::NoType;
// Grabmouse and hidecursor introduce some lag into the mouse movement,
// so we need to fudge the numbers a bit
if(theGrabMouseIndicator && theHideCursorIndicator)
{
mouseX = (int)((float)mouseX + (float)event.motion.xrel
* 1.5 * (float) zoom);
}
else
{
mouseX = mouseX + event.motion.xrel * zoom;
}
// Check to make sure mouseX is within the game window
if(mouseX < 0)
mouseX = 0;
else if(mouseX > width)
mouseX = width;
resistance = (Int32)(1000000.0 * (width - mouseX) / width);
// Now, set the event of the correct paddle to the calculated resistance
if(thePaddleMode == 0)
type = Event::PaddleZeroResistance;
else if(thePaddleMode == 1)
type = Event::PaddleOneResistance;
else if(thePaddleMode == 2)
type = Event::PaddleTwoResistance;
else if(thePaddleMode == 3)
type = Event::PaddleThreeResistance;
theConsole->eventHandler().sendEvent(type, resistance);
}
else if(event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP)
{
Event::Type type = Event::LastType;
Int32 value;
if(event.type == SDL_MOUSEBUTTONDOWN)
value = 1;
else
value = 0;
if(thePaddleMode == 0)
type = Event::PaddleZeroFire;
else if(thePaddleMode == 1)
type = Event::PaddleOneFire;
else if(thePaddleMode == 2)
type = Event::PaddleTwoFire;
else if(thePaddleMode == 3)
type = Event::PaddleThreeFire;
theConsole->eventHandler().sendEvent(type, value);
}
else if(event.type == SDL_ACTIVEEVENT)
{
if((event.active.state & SDL_APPACTIVE) && (event.active.gain == 0))
{
if(!theConsole->eventHandler().doPause())
{
theConsole->eventHandler().sendEvent(Event::Pause, 1);
}
}
}
else if(event.type == SDL_QUIT)
{
theConsole->eventHandler().sendEvent(Event::Quit, 1);
}
else if(event.type == SDL_VIDEOEXPOSE)
{
theDisplay->refresh();
}
#ifdef JOYSTICK_SUPPORT
// Read joystick events and modify event states
StellaEvent::JoyStick stick;
StellaEvent::JoyCode code;
Int32 state;
Uint8 axis;
Uint8 button;
Int32 resistance;
Sint16 value;
JoyType type;
if(event.jbutton.which >= StellaEvent::LastJSTICK)
return;
stick = joyList[event.jbutton.which];
type = theJoysticks[event.jbutton.which].type;
// Figure put what type of joystick we're dealing with
// Stelladaptors behave differently, and can't be remapped
switch(type)
{
case JT_NONE:
break;
case JT_REGULAR:
if((event.type == SDL_JOYBUTTONDOWN) || (event.type == SDL_JOYBUTTONUP))
{
if(event.jbutton.button >= StellaEvent::LastJCODE)
return;
code = joyButtonList[event.jbutton.button];
state = event.jbutton.state == SDL_PRESSED ? 1 : 0;
theConsole->eventHandler().sendJoyEvent(stick, code, state);
}
else if(event.type == SDL_JOYAXISMOTION)
{
axis = event.jaxis.axis;
value = event.jaxis.value;
if(axis == 0) // x-axis
{
theConsole->eventHandler().sendJoyEvent(stick, StellaEvent::JAXIS_LEFT,
(value < -16384) ? 1 : 0);
theConsole->eventHandler().sendJoyEvent(stick, StellaEvent::JAXIS_RIGHT,
(value > 16384) ? 1 : 0);
}
else if(axis == 1) // y-axis
{
theConsole->eventHandler().sendJoyEvent(stick, StellaEvent::JAXIS_UP,
(value < -16384) ? 1 : 0);
theConsole->eventHandler().sendJoyEvent(stick, StellaEvent::JAXIS_DOWN,
(value > 16384) ? 1 : 0);
}
}
break; // Regular joystick
case JT_STELLADAPTOR_1:
case JT_STELLADAPTOR_2:
if((event.type == SDL_JOYBUTTONDOWN) || (event.type == SDL_JOYBUTTONUP))
{
button = event.jbutton.button;
state = event.jbutton.state == SDL_PRESSED ? 1 : 0;
// Send button events for the joysticks/paddles
if(button == 0)
{
if(type == JT_STELLADAPTOR_1)
{
theConsole->eventHandler().sendEvent(Event::JoystickZeroFire, state);
theConsole->eventHandler().sendEvent(Event::PaddleZeroFire, state);
}
else
{
theConsole->eventHandler().sendEvent(Event::JoystickOneFire, state);
theConsole->eventHandler().sendEvent(Event::PaddleTwoFire, state);
}
}
else if(button == 1)
{
if(type == JT_STELLADAPTOR_1)
theConsole->eventHandler().sendEvent(Event::PaddleOneFire, state);
else
theConsole->eventHandler().sendEvent(Event::PaddleThreeFire, state);
}
}
else if(event.type == SDL_JOYAXISMOTION)
{
axis = event.jaxis.axis;
value = event.jaxis.value;
// Send axis events for the joysticks
theConsole->eventHandler().sendEvent(SA_Axis[type-2][axis][0],
(value < -16384) ? 1 : 0);
theConsole->eventHandler().sendEvent(SA_Axis[type-2][axis][1],
(value > 16384) ? 1 : 0);
// Send axis events for the paddles
resistance = (Int32) (1000000.0 * (32767 - value) / 65534);
theConsole->eventHandler().sendEvent(SA_Axis[type-2][axis][2], resistance);
}
break; // Stelladaptor joystick
default:
break;
}
#endif
}
}
/**
Setup the properties set by first checking for a user file
"$HOME/.stella/stella.pro", then a system-wide file "/etc/stella.pro".
Return false if neither file is found, else return true.
@param set The properties set to setup
*/
bool setupProperties(PropertiesSet& set)
{
bool useMemList = false;
string theAlternateProFile = theSettings->getString("altpro");
string theUserProFile = theSettings->userPropertiesFilename();
string theSystemProFile = theSettings->systemPropertiesFilename();
#ifdef DEVELOPER_SUPPORT
// If the user wishes to merge any property modifications to the
// PropertiesSet file, then the PropertiesSet file MUST be loaded
// into memory.
useMemList = theSettings->getBool("mergeprops");
#endif
// Check to see if the user has specified an alternate .pro file.
if(theAlternateProFile != "")
{
set.load(theAlternateProFile, &Console::defaultProperties(), useMemList);
return true;
}
if(theUserProFile != "")
{
set.load(theUserProFile, &Console::defaultProperties(), useMemList);
return true;
}
else if(theSystemProFile != "")
{
set.load(theSystemProFile, &Console::defaultProperties(), useMemList);
return true;
}
else
{
set.load("", &Console::defaultProperties(), false);
return true;
}
}
/**
Does general cleanup in case any operation failed (or at end of program).
*/
void cleanup()
{
#ifdef JOYSTICK_SUPPORT
for(uInt32 i = 0; i < StellaEvent::LastJSTICK; i++)
{
if(SDL_JoystickOpened(i))
SDL_JoystickClose(theJoysticks[i].stick);
}
#endif
if(theSettings)
delete theSettings;
if(theConsole)
delete theConsole;
if(theSound)
delete theSound;
if(theDisplay)
delete theDisplay;
SDL_Quit();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int main(int argc, char* argv[])
{
#if defined(UNIX)
theSettings = new SettingsUNIX();
#elif defined(WIN32)
theSettings = new SettingsWin32();
#else
#error Unsupported platform!
#endif
if(!theSettings)
{
cleanup();
return 0;
}
theSettings->loadConfig();
// Take care of commandline arguments
if(!theSettings->loadCommandLine(argc, argv))
{
string message = "Stella version 1.4_cvs\n\nUsage: stella [options ...] romfile";
theSettings->usage(message);
cleanup();
return 0;
}
// Cache some settings so they don't have to be repeatedly searched for
thePaddleMode = theSettings->getInt("paddle");
theShowInfoFlag = theSettings->getBool("showinfo");
theGrabMouseIndicator = theSettings->getBool("grabmouse");
theHideCursorIndicator = theSettings->getBool("hidecursor");
// Request that the SDL window be centered, if possible
putenv("SDL_VIDEO_CENTERED=1");
// Get a pointer to the file which contains the cartridge ROM
const char* file = argv[argc - 1];
// Open the cartridge image and read it in
ifstream in(file, ios_base::binary);
if(!in)
{
cerr << "ERROR: Couldn't open " << file << "..." << endl;
cleanup();
return 0;
}
uInt8* image = new uInt8[512 * 1024];
in.read((char*)image, 512 * 1024);
uInt32 size = in.gcount();
in.close();
// Create a properties set for us to use and set it up
PropertiesSet propertiesSet;
if(!setupProperties(propertiesSet))
{
delete[] image;
cleanup();
return 0;
}
// Create an SDL window
string videodriver = theSettings->getString("video");
if(videodriver == "soft")
{
theDisplay = new FrameBufferSoft();
if(theShowInfoFlag)
cout << "Using software mode for video.\n";
}
#ifdef DISPLAY_OPENGL
else if(videodriver == "gl")
{
theDisplay = new FrameBufferGL();
theUseOpenGLFlag = true;
if(theShowInfoFlag)
cout << "Using OpenGL mode for video.\n";
}
#endif
else // a driver that doesn't exist was requested, so use software mode
{
theDisplay = new FrameBufferSoft();
if(theShowInfoFlag)
cout << "Using software mode for video.\n";
}
if(!theDisplay)
{
cerr << "ERROR: Couldn't set up display.\n";
delete[] image;
cleanup();
return 0;
}
// Create a sound object for playing audio
if(theSettings->getBool("sound"))
{
uInt32 fragsize = theSettings->getInt("fragsize");
uInt32 bufsize = theSettings->getInt("bufsize");
theSound = new SoundSDL(fragsize, bufsize);
if(theShowInfoFlag)
{
cout << "Sound enabled, using fragment size = " << fragsize;
cout << " and buffer size = " << bufsize << "." << endl;
}
}
else // even if sound has been disabled, we still need a sound object
{
theSound = new Sound();
if(theShowInfoFlag)
cout << "Sound disabled.\n";
}
theSound->setVolume(theSettings->getInt("volume"));
// Get just the filename of the file containing the ROM image
const char* filename = (!strrchr(file, '/')) ? file : strrchr(file, '/') + 1;
// Create the 2600 game console
theConsole = new Console(image, size, filename, *theSettings, propertiesSet,
*theDisplay, *theSound);
// Free the image since we don't need it any longer
delete[] image;
// Setup the SDL joysticks
// This must be done after the console is created
if(!setupJoystick())
{
cerr << "ERROR: Couldn't set up joysticks.\n";
cleanup();
return 0;
}
// These variables are common to both timing options
// and are needed to calculate the overall frames per second.
uInt32 frameTime = 0, numberOfFrames = 0;
if(theSettings->getBool("accurate")) // normal, CPU-intensive timing
{
// Set up accurate timing stuff
uInt32 startTime, delta;
uInt32 timePerFrame = (uInt32)(1000000.0 / (double)theSettings->getInt("framerate"));
// Set the base for the timers
frameTime = 0;
// Main game loop
for(;;)
{
// Exit if the user wants to quit
if(theConsole->eventHandler().doQuit())
{
break;
}
startTime = getTicks();
handleEvents();
theConsole->update();
// Now, waste time if we need to so that we are at the desired frame rate
for(;;)
{
delta = getTicks() - startTime;
if(delta >= timePerFrame)
break;
}
frameTime += getTicks() - startTime;
++numberOfFrames;
}
}
else // less accurate, less CPU-intensive timing
{
// Set up less accurate timing stuff
uInt32 startTime, virtualTime, currentTime;
uInt32 timePerFrame = (uInt32)(1000000.0 / (double)theSettings->getInt("framerate"));
// Set the base for the timers
virtualTime = getTicks();
frameTime = 0;
// Main game loop
for(;;)
{
// Exit if the user wants to quit
if(theConsole->eventHandler().doQuit())
{
break;
}
startTime = getTicks();
handleEvents();
theConsole->update();
currentTime = getTicks();
virtualTime += timePerFrame;
if(currentTime < virtualTime)
{
SDL_Delay((virtualTime - currentTime)/1000);
}
currentTime = getTicks() - startTime;
frameTime += currentTime;
++numberOfFrames;
}
}
if(theShowInfoFlag)
{
double executionTime = (double) frameTime / 1000000.0;
double framesPerSecond = (double) numberOfFrames / executionTime;
cout << endl;
cout << numberOfFrames << " total frames drawn\n";
cout << framesPerSecond << " frames/second\n";
cout << endl;
cout << "Cartridge Name: " << theConsole->properties().get("Cartridge.Name");
cout << endl;
cout << "Cartridge MD5: " << theConsole->properties().get("Cartridge.MD5");
cout << endl << endl;
}
// Cleanup time ...
cleanup();
return 0;
}

View File

@ -1,38 +0,0 @@
/* XPM */
static char * stella_icon[] = {
"32 32 3 1",
" c None",
". c #000000",
"+ c #FFFFFF",
" ",
" ",
" ",
" ",
" .............. ",
" .............. ",
" ..++..++..++.. ",
" ..++..++..++.. ",
" ..++..++..++.. ",
" ..++..++..++.. ",
" ..++..++..++.. ",
" ..++..++..++.. ",
" ..++..++..++.. ",
" ..++..++..++.. ",
" ....++..++..++.... ",
" ....++..++..++.... ",
" ....++++..++..++++.... ",
" ....++++..++..++++.... ",
" ..++++....++....++++.. ",
" ..++++....++....++++.. ",
" ......++......++......++...... ",
" ......++......++......++...... ",
" ..++++++.. ..++.. ..++++++.. ",
" ..++++++.. ..++.. ..++++++.. ",
" ..++++.... ..++.. ....++++.. ",
" ..++++.... ..++.. ....++++.. ",
" ........ ...... ........ ",
" ........ ...... ........ ",
" ",
" ",
" ",
" "};

View File

@ -1,296 +0,0 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2002 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: SoundALSA.cxx,v 1.6 2003-11-19 15:57:10 stephena Exp $
//============================================================================
#include <alsa/asoundlib.h>
#include <stdio.h>
#include "SoundALSA.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundALSA::SoundALSA()
: myIsInitializedFlag(false),
myPcmHandle(0),
myMixerHandle(0),
myMixerElem(0),
myOriginalVolumeLeft(-1),
myOriginalVolumeRight(-1),
myBufferSize(0),
mySampleRate(0)
{
Int32 err;
char pcmName[] = "plughw:0,0";
char mixerName[] = "PCM";
char mixerCard[] = "default";
snd_pcm_hw_params_t* hwparams;
// Allocate the snd_pcm_hw_params_t structure on the stack
snd_pcm_hw_params_alloca(&hwparams);
// Open the PCM device for writing
if((err = snd_pcm_open(&myPcmHandle, pcmName, SND_PCM_STREAM_PLAYBACK, 0)) < 0)
{
alsaError(err);
return;
}
// Init hwparams with full configuration space
if((err = snd_pcm_hw_params_any(myPcmHandle, hwparams)) < 0)
{
alsaError(err);
return;
}
// Set interleaved access
if((err = snd_pcm_hw_params_set_access(myPcmHandle, hwparams,
SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
{
alsaError(err);
return;
}
// Set the audio data format
if((err = snd_pcm_hw_params_set_format(myPcmHandle, hwparams, SND_PCM_FORMAT_U8)) < 0)
{
alsaError(err);
return;
}
// Set the number of audio channels to 1 (mono mode)
if((err = snd_pcm_hw_params_set_channels(myPcmHandle, hwparams, 1)) < 0)
{
alsaError(err);
return;
}
// Set the audio sample rate. If the exact rate is not supported by
// the hardware, use nearest possible rate
mySampleRate = 31400;
if((err = snd_pcm_hw_params_set_rate_near(myPcmHandle, hwparams, mySampleRate, 0)) < 0)
{
alsaError(err);
return;
}
// Set number of fragments to 2
if((err = snd_pcm_hw_params_set_periods(myPcmHandle, hwparams, 2, 0)) < 0)
{
alsaError(err);
return;
}
// Set size of fragments to 512 bytes
myBufferSize = 512;
if((err = snd_pcm_hw_params_set_period_size(myPcmHandle, hwparams,
myBufferSize, 0)) < 0)
{
alsaError(err);
return;
}
// Apply HW parameter settings to PCM device
if((err = snd_pcm_hw_params(myPcmHandle, hwparams)) < 0)
{
alsaError(err);
return;
}
////////////////////////////////////////////////////////////
// Now, open the mixer so we'll be able to change the volume
////////////////////////////////////////////////////////////
snd_mixer_selem_id_t* mixerID;
// Allocate simple mixer ID
snd_mixer_selem_id_alloca(&mixerID);
// Sets simple mixer ID and name
snd_mixer_selem_id_set_index(mixerID, 0);
snd_mixer_selem_id_set_name(mixerID, mixerName);
// Open the mixer device
if((err = snd_mixer_open(&myMixerHandle, 0)) < 0)
{
alsaError(err);
return;
}
// Attach the mixer to the default sound card
if((err = snd_mixer_attach(myMixerHandle, mixerCard)) < 0)
{
alsaError(err);
return;
}
// Register the mixer with the sound system
if((err = snd_mixer_selem_register(myMixerHandle, NULL, NULL)) < 0)
{
alsaError(err);
return;
}
if((err = snd_mixer_load(myMixerHandle)) < 0)
{
alsaError(err);
return;
}
// Get the mixer element that will be used to control volume
if((myMixerElem = snd_mixer_find_selem(myMixerHandle, mixerID)) == 0)
{
alsaError(err);
return;
}
// Save the original volume so we can restore it on exit
snd_mixer_selem_get_playback_volume(myMixerElem, (_snd_mixer_selem_channel_id) 0,
&myOriginalVolumeLeft);
snd_mixer_selem_get_playback_volume(myMixerElem, (_snd_mixer_selem_channel_id) 1,
&myOriginalVolumeRight);
// Prepare the audio device for playback
snd_pcm_prepare(myPcmHandle);
// Indicate that the sound system is fully initialized
myIsInitializedFlag = true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundALSA::~SoundALSA()
{
closeDevice();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundALSA::closeDevice()
{
if(myIsInitializedFlag)
{
// Restore original volume
if(myMixerHandle)
{
if((myOriginalVolumeLeft != -1) && (myOriginalVolumeRight != -1))
{
snd_mixer_selem_set_playback_volume(myMixerElem, (_snd_mixer_selem_channel_id) 0,
myOriginalVolumeLeft);
snd_mixer_selem_set_playback_volume(myMixerElem, (_snd_mixer_selem_channel_id) 1,
myOriginalVolumeRight);
}
snd_mixer_close(myMixerHandle);
}
if(myPcmHandle)
snd_pcm_close(myPcmHandle);
}
myIsInitializedFlag = false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 SoundALSA::getSampleRate() const
{
return myIsInitializedFlag ? mySampleRate : 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SoundALSA::isSuccessfullyInitialized() const
{
return myIsInitializedFlag;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundALSA::setVolume(Int32 percent)
{
if(myIsInitializedFlag && myMixerElem)
{
if((percent >= 0) && (percent <= 100))
{
long int lowerBound, upperBound, newVolume;
snd_mixer_selem_get_playback_volume_range(myMixerElem, &lowerBound, &upperBound);
newVolume = (long int) (((upperBound - lowerBound) * percent / 100.0) + lowerBound);
snd_mixer_selem_set_playback_volume(myMixerElem, (_snd_mixer_selem_channel_id) 0,
newVolume);
snd_mixer_selem_set_playback_volume(myMixerElem, (_snd_mixer_selem_channel_id) 1,
newVolume);
}
else if(percent == -1) // If -1 has been specified, play sound at default volume
{
myOriginalVolumeRight = myOriginalVolumeRight = -1;
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundALSA::update()
{
if(myIsInitializedFlag)
{
if(myPauseStatus)
return;
snd_pcm_sframes_t frames;
uInt8 periodCount = 0;
// Dequeue samples as long as full fragments are available
while(myMediaSource->numberOfAudioSamples() >= myBufferSize)
{
uInt8 buffer[myBufferSize];
myMediaSource->dequeueAudioSamples(buffer, myBufferSize);
if((frames = snd_pcm_writei(myPcmHandle, buffer, myBufferSize)) == -EPIPE)
{
snd_pcm_prepare(myPcmHandle);
break;
}
periodCount++;
}
// Fill any unused fragments with silence so that we have a lower
// risk of having playback underruns
for(int i = 0; i < 1-periodCount; ++i)
{
frames = snd_pcm_avail_update(myPcmHandle);
if (frames > 0)
{
uInt8 buffer[frames];
memset((void*)buffer, 0, frames);
snd_pcm_writei(myPcmHandle, buffer, frames);
}
else if(frames == -EPIPE) // this should never happen
{
cerr << "EPIPE after write\n";
break;
}
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundALSA::alsaError(Int32 error)
{
cerr << "SoundALSA: " << snd_strerror(error) << endl;
if(myMixerHandle)
snd_mixer_close(myMixerHandle);
if(myPcmHandle)
snd_pcm_close(myPcmHandle);
mySampleRate = 0;
}

View File

@ -1,114 +0,0 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2002 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: SoundALSA.hxx,v 1.6 2003-11-19 15:57:11 stephena Exp $
//============================================================================
#ifndef SOUNDALSA_HXX
#define SOUNDALSA_HXX
#include <alsa/asoundlib.h>
#include "Sound.hxx"
#include "bspf.hxx"
#include "Console.hxx"
#include "MediaSrc.hxx"
/**
This class implements a sound class using the
Advanced Linux Sound Architecture (ALSA) version 0.9.x API.
@author Stephen Anthony
@version $Id: SoundALSA.hxx,v 1.6 2003-11-19 15:57:11 stephena Exp $
*/
class SoundALSA : public Sound
{
public:
/**
Create a new sound object
*/
SoundALSA();
/**
Destructor
*/
virtual ~SoundALSA();
public:
/**
Closes the sound device
*/
void closeDevice();
/**
Return the playback sample rate for the sound device.
@return The playback sample rate
*/
uInt32 getSampleRate() const;
/**
Return true iff the sound device was successfully initlaized.
@return true iff the sound device was successfully initlaized.
*/
bool isSuccessfullyInitialized() const;
/**
Sets the volume of the sound device to the specified level. The
volume is given as a percentage from 0 to 100. A -1 indicates
that the volume shouldn't be changed at all.
@param percent The new volume percentage level for the sound device
*/
void setVolume(Int32 percent);
/**
Update the sound device using the audio sample from the
media source.
*/
void update();
private:
/**
Prints the given error message, and frees any resources used.
*/
void alsaError(Int32 error);
private:
// Indicates if the sound device was successfully initialized
bool myIsInitializedFlag;
// Handle for the PCM device
snd_pcm_t* myPcmHandle;
// Handle for the mixer device
snd_mixer_t* myMixerHandle;
// Mixer elem, used to set volume
snd_mixer_elem_t* myMixerElem;
// Original mixer volume when the sound device was opened
long int myOriginalVolumeLeft;
long int myOriginalVolumeRight;
// PCM buffer size
uInt32 myBufferSize;
// PCM sample rate
uInt32 mySampleRate;
};
#endif

View File

@ -1,240 +0,0 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2002 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: SoundOSS.cxx,v 1.5 2003-11-19 15:57:11 stephena Exp $
//============================================================================
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#ifdef __FreeBSD__
#include <machine/soundcard.h>
#else
#include <sys/soundcard.h>
#endif
#define DSP_DEVICE "/dev/dsp"
#define MIXER_DEVICE "/dev/mixer"
#include "SoundOSS.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundOSS::SoundOSS()
: myIsInitializedFlag(false),
myDspFd(-1),
myMixerFd(-1),
myOriginalVolume(-1),
mySampleRate(0)
{
// Open the sound device for writing
if((myDspFd = open(DSP_DEVICE, O_WRONLY, 0)) == -1)
{
perror(DSP_DEVICE);
return;
}
// Set the number and size of fragments
int numberAndSizeOfFragments = 0x00020009;
if(ioctl(myDspFd, SNDCTL_DSP_SETFRAGMENT, &numberAndSizeOfFragments) == -1)
{
perror(DSP_DEVICE);
close(myDspFd);
myDspFd = -1;
return;
}
// Set the audio data format
int format = AFMT_U8;
if(ioctl(myDspFd, SNDCTL_DSP_SETFMT, &format) == -1)
{
perror(DSP_DEVICE);
close(myDspFd);
myDspFd = -1;
return;
}
// Make sure the U8 format was selected
if(format != AFMT_U8)
{
cerr << DSP_DEVICE << ": Doesn't support 8-bit sample format!" << endl;
close(myDspFd);
myDspFd = -1;
return;
}
// Set the number of audio channels to 1 (mono mode)
int channels = 1;
if(ioctl(myDspFd, SNDCTL_DSP_CHANNELS, &channels) == -1)
{
perror(DSP_DEVICE);
close(myDspFd);
myDspFd = -1;
return;
}
// Make sure mono mode was selected
if(channels != 1)
{
cerr << DSP_DEVICE << ": Doesn't support mono mode!" << endl;
close(myDspFd);
myDspFd = -1;
return;
}
// Set the audio sample rate
mySampleRate = 31400;
if(ioctl(myDspFd, SNDCTL_DSP_SPEED, &mySampleRate) == -1)
{
perror(DSP_DEVICE);
close(myDspFd);
myDspFd = -1;
mySampleRate = 0;
return;
}
// Now, open the mixer so we'll be able to change the volume
if((myMixerFd = open(MIXER_DEVICE, O_RDWR, 0)) == -1)
{
perror(MIXER_DEVICE);
}
else
{
if(ioctl(myMixerFd, MIXER_READ(SOUND_MIXER_PCM), &myOriginalVolume) == -1)
{
myOriginalVolume = -1;
}
}
// Indicate that the sound system is fully initialized
myIsInitializedFlag = true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundOSS::~SoundOSS()
{
closeDevice();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundOSS::closeDevice()
{
if(myIsInitializedFlag)
{
if(myMixerFd != -1)
{
if(myOriginalVolume != -1)
{
if(ioctl(myMixerFd, MIXER_WRITE(SOUND_MIXER_PCM),
&myOriginalVolume) == -1)
{
perror(MIXER_DEVICE);
}
}
close(myMixerFd);
}
if(myDspFd != -1)
{
close(myDspFd);
}
myIsInitializedFlag = false;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 SoundOSS::getSampleRate() const
{
return myIsInitializedFlag ? mySampleRate : 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SoundOSS::isSuccessfullyInitialized() const
{
return myIsInitializedFlag;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundOSS::setVolume(Int32 percent)
{
if(myIsInitializedFlag && (myMixerFd != -1))
{
if((percent >= 0) && (percent <= 100))
{
int v = percent | (percent << 8);
if(ioctl(myMixerFd, MIXER_WRITE(SOUND_MIXER_PCM), &v) == -1)
{
perror(MIXER_DEVICE);
close(myMixerFd);
myMixerFd = -1;
}
}
else if(percent == -1) // If -1 has been specified, play sound at default volume
{
myOriginalVolume = -1;
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundOSS::update()
{
if(myIsInitializedFlag)
{
if(myPauseStatus)
return;
// Get audio buffer information
audio_buf_info info;
if(ioctl(myDspFd, SNDCTL_DSP_GETOSPACE, &info) == -1)
{
return;
}
// Dequeue samples as long as full fragments are available
while(myMediaSource->numberOfAudioSamples() >= (uInt32)info.fragsize)
{
uInt8 buffer[info.fragsize];
myMediaSource->dequeueAudioSamples(buffer, (uInt32)info.fragsize);
write(myDspFd, buffer, info.fragsize);
}
// Fill any unused fragments with silence so that we have a lower
// risk of having playback underruns
for(;;)
{
// Get audio buffer information
if(ioctl(myDspFd, SNDCTL_DSP_GETOSPACE, &info) == -1)
{
return;
}
if(info.fragments > 0)
{
uInt8 buffer[info.fragsize];
memset((void*)buffer, 0, info.fragsize);
write(myDspFd, buffer, info.fragsize);
}
else
{
break;
}
}
}
}

View File

@ -1,97 +0,0 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2002 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: SoundOSS.hxx,v 1.5 2003-11-19 15:57:11 stephena Exp $
//============================================================================
#ifndef SOUNDOSS_HXX
#define SOUNDOSS_HXX
#include "Sound.hxx"
#include "bspf.hxx"
#include "MediaSrc.hxx"
/**
This class implements a sound class using the
Open Sound System (OSS) API.
@author Bradford W. Mott
@version $Id: SoundOSS.hxx,v 1.5 2003-11-19 15:57:11 stephena Exp $
*/
class SoundOSS : public Sound
{
public:
/**
Create a new sound object
*/
SoundOSS();
/**
Destructor
*/
virtual ~SoundOSS();
public:
/**
Closes the sound device
*/
void closeDevice();
/**
Return the playback sample rate for the sound device.
@return The playback sample rate
*/
uInt32 getSampleRate() const;
/**
Return true iff the sound device was successfully initlaized.
@return true iff the sound device was successfully initlaized.
*/
bool isSuccessfullyInitialized() const;
/**
Sets the volume of the sound device to the specified level. The
volume is given as a percentage from 0 to 100. A -1 indicates
that the volume shouldn't be changed at all.
@param percent The new volume percentage level for the sound device
*/
void setVolume(Int32 percent);
/**
Update the sound device using the audio sample from the
media source.
*/
void update();
private:
// Indicates if the sound device was successfully initialized
bool myIsInitializedFlag;
// DSP file descriptor
int myDspFd;
// Mixer file descriptor
int myMixerFd;
// Original mixer volume when the sound device was opened
int myOriginalVolume;
// DSP sample rate
uInt32 mySampleRate;
};
#endif

View File

@ -1,408 +0,0 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2002 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: SoundSDL.cxx,v 1.10 2004-04-27 00:50:52 stephena Exp $
//============================================================================
#include <SDL.h>
#include "TIASound.h"
#include "Serializer.hxx"
#include "Deserializer.hxx"
#include "System.hxx"
#include "SoundSDL.hxx"
//#define DIGITAL_SOUND
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundSDL::SoundSDL(uInt32 fragsize, uInt32 queuesize)
: myCurrentVolume(SDL_MIX_MAXVOLUME),
myFragmentSize(fragsize),
myIsInitializedFlag(false),
myIsMuted(false),
mySampleRate(31400),
mySampleQueue(queuesize)
{
if(1)
{
if(SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
{
cerr << "WARNING: Couldn't initialize SDL audio system! " << endl;
cerr << " " << SDL_GetError() << endl;
myIsInitializedFlag = false;
mySampleRate = 0;
return;
}
SDL_AudioSpec desired;
desired.freq = mySampleRate;
desired.format = AUDIO_U8;
desired.channels = 1;
desired.samples = myFragmentSize;
desired.callback = callback;
desired.userdata = (void*)this;
if(SDL_OpenAudio(&desired, &myHardwareSpec) < 0)
{
cerr << "WARNING: Couldn't open SDL audio system! " << endl;
cerr << " " << SDL_GetError() << endl;
myIsInitializedFlag = false;
mySampleRate = 0;
return;
}
// Make sure the sample buffer isn't to big (if it is the sound code
// will not work so we'll need to disable the audio support)
if(((float)myHardwareSpec.size / (float)myHardwareSpec.freq) >= 0.25)
{
cerr << "WARNING: Audio device doesn't support real time audio! Make ";
cerr << "sure a sound" << endl;
cerr << " server isn't running. Audio is disabled..." << endl;
SDL_CloseAudio();
myIsInitializedFlag = false;
mySampleRate = 0;
return;
}
myIsInitializedFlag = true;
myIsMuted = false;
mySampleRate = myHardwareSpec.freq;
myFragmentSize = myHardwareSpec.samples;
// cerr << "Freq: " << (int)mySampleRate << endl;
// cerr << "Format: " << (int)myHardwareSpec.format << endl;
// cerr << "Channels: " << (int)myHardwareSpec.channels << endl;
// cerr << "Silence: " << (int)myHardwareSpec.silence << endl;
// cerr << "Samples: " << (int)myHardwareSpec.samples << endl;
// cerr << "Size: " << (int)myHardwareSpec.size << endl;
// Now initialize the TIASound object which will actually generate sound
Tia_sound_init(31400, mySampleRate);
// And start the SDL sound subsystem ...
SDL_PauseAudio(0);
}
else
{
myIsInitializedFlag = false;
myIsMuted = true;
mySampleRate = 0;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundSDL::~SoundSDL()
{
if(myIsInitializedFlag)
{
SDL_CloseAudio();
}
myIsInitializedFlag = false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SoundSDL::isSuccessfullyInitialized() const
{
return myIsInitializedFlag;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::mute(bool state)
{
if(!myIsInitializedFlag)
{
return;
}
// Ignore multiple calls to do the same thing
if(myIsMuted == state)
{
return;
}
myIsMuted = state;
SDL_PauseAudio(myIsMuted ? 1 : 0);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::setVolume(Int32 percent)
{
if(myIsInitializedFlag)
{
if((percent >= 0) && (percent <= 100))
{
SDL_LockAudio();
myCurrentVolume = (uInt32)(((float)percent / 100.0) * SDL_MIX_MAXVOLUME);
SDL_UnlockAudio();
}
else if(percent == -1) // If -1 has been specified, play sound at default volume
{
myCurrentVolume = SDL_MIX_MAXVOLUME;
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::update()
{
#if !defined(DIGITAL_SOUND)
if(!myPauseStatus && myIsInitializedFlag)
{
// Make sure we have exclusive access to the sample queue
// SDL_LockAudio();
// Generate enough samples to keep the sample queue full to capacity
uInt32 numbytes = mySampleQueue.capacity() - mySampleQueue.size();
uInt8 buffer[numbytes];
Tia_process(buffer, numbytes);
mySampleQueue.enqueue(buffer, numbytes);
// Release lock on the sample queue
// SDL_UnlockAudio();
}
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::set(uInt16 addr, uInt8 value)
{
#if defined(DIGITAL_SOUND)
// Calculate the number of samples that need to be generated based on the
// number of CPU cycles which have passed since the last sound update
uInt32 samplesToGenerate =
(mySampleRate * (mySystem->cycles() - myLastSoundUpdateCycle)) / 1190000;
// Update counters and create samples if there's one sample to generate
// TODO: This doesn't handle rounding quite right (10/08/2002)
if(samplesToGenerate >= 1)
{
uInt8 buffer[1024];
for(Int32 sg = (Int32)samplesToGenerate; sg > 0; sg -= 1024)
{
Tia_process(buffer, ((sg >= 1024) ? 1024 : sg));
mySampleQueue.enqueue(buffer, ((sg >= 1024) ? 1024 : sg));
}
myLastSoundUpdateCycle = myLastSoundUpdateCycle +
((samplesToGenerate * 1190000) / mySampleRate);
}
if(addr != 0)
{
Update_tia_sound(addr, value);
}
#else
Update_tia_sound(addr, value);
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SoundSDL::save(Serializer& out)
{
string device = "TIASound";
try
{
out.putString(device);
uInt8 reg1 = 0, reg2 = 0, reg3 = 0, reg4 = 0, reg5 = 0, reg6 = 0;
// Only get the TIA sound registers if sound is enabled
if(myIsInitializedFlag)
Tia_get_registers(&reg1, &reg2, &reg3, &reg4, &reg5, &reg6);
out.putLong(reg1);
out.putLong(reg2);
out.putLong(reg3);
out.putLong(reg4);
out.putLong(reg5);
out.putLong(reg6);
out.putLong(myLastSoundUpdateCycle);
}
catch(char *msg)
{
cerr << msg << endl;
return false;
}
catch(...)
{
cerr << "Unknown error in save state for " << device << endl;
return false;
}
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SoundSDL::load(Deserializer& in)
{
string device = "TIASound";
try
{
if(in.getString() != device)
return false;
uInt8 reg1 = 0, reg2 = 0, reg3 = 0, reg4 = 0, reg5 = 0, reg6 = 0;
reg1 = (uInt8) in.getLong();
reg2 = (uInt8) in.getLong();
reg3 = (uInt8) in.getLong();
reg4 = (uInt8) in.getLong();
reg5 = (uInt8) in.getLong();
reg6 = (uInt8) in.getLong();
myLastSoundUpdateCycle = (Int32) in.getLong();
// Only update the TIA sound registers if sound is enabled
if(myIsInitializedFlag)
Tia_set_registers(reg1, reg2, reg3, reg4, reg5, reg6);
}
catch(char *msg)
{
cerr << msg << endl;
return false;
}
catch(...)
{
cerr << "Unknown error in load state for " << device << endl;
return false;
}
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::callback(void* udata, uInt8* stream, int len)
{
SoundSDL* sound = (SoundSDL*)udata;
if(!sound->isSuccessfullyInitialized())
{
return;
}
if(sound->mySampleQueue.size() > 0)
{
Int32 offset;
uInt8 buffer[2048];
for(offset = 0; (offset < len) && (sound->mySampleQueue.size() > 0); )
{
uInt32 s = sound->mySampleQueue.dequeue(buffer,
(2048 > (len - offset) ? (len - offset) : 2048));
SDL_MixAudio(stream + offset, buffer, s, sound->myCurrentVolume);
offset += s;
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundSDL::SampleQueue::SampleQueue(uInt32 capacity)
: myCapacity(capacity),
myBuffer(0),
mySize(0),
myHead(0),
myTail(0)
{
myBuffer = new uInt8[myCapacity];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundSDL::SampleQueue::~SampleQueue()
{
delete[] myBuffer;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::SampleQueue::clear()
{
myHead = myTail = mySize = 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 SoundSDL::SampleQueue::dequeue(uInt8* buffer, uInt32 size)
{
// We can only dequeue up to the number of items in the queue
if(size > mySize)
{
size = mySize;
}
if((myHead + size) < myCapacity)
{
memcpy((void*)buffer, (const void*)(myBuffer + myHead), size);
myHead += size;
}
else
{
uInt32 s1 = myCapacity - myHead;
uInt32 s2 = size - s1;
memcpy((void*)buffer, (const void*)(myBuffer + myHead), s1);
memcpy((void*)(buffer + s1), (const void*)myBuffer, s2);
myHead = (myHead + size) % myCapacity;
}
mySize -= size;
return size;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::SampleQueue::enqueue(uInt8* buffer, uInt32 size)
{
// If an attempt is made to enqueue more than the queue can hold then
// we'll only enqueue the last myCapacity elements.
if(size > myCapacity)
{
buffer += (size - myCapacity);
size = myCapacity;
}
if((myTail + size) < myCapacity)
{
memcpy((void*)(myBuffer + myTail), (const void*)buffer, size);
myTail += size;
}
else
{
uInt32 s1 = myCapacity - myTail;
uInt32 s2 = size - s1;
memcpy((void*)(myBuffer + myTail), (const void*)buffer, s1);
memcpy((void*)myBuffer, (const void*)(buffer + s1), s2);
myTail = (myTail + size) % myCapacity;
}
if((mySize + size) > myCapacity)
{
myHead = (myHead + ((mySize + size) - myCapacity)) % myCapacity;
mySize = myCapacity;
}
else
{
mySize += size;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 SoundSDL::SampleQueue::size() const
{
return mySize;
}

View File

@ -1,204 +0,0 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2002 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: SoundSDL.hxx,v 1.7 2004-04-04 02:03:15 stephena Exp $
//============================================================================
#ifndef SOUNDSDL_HXX
#define SOUNDSDL_HXX
#include <SDL.h>
#include "Sound.hxx"
#include "bspf.hxx"
#include "MediaSrc.hxx"
/**
This class implements the sound API for SDL.
@author Stephen Anthony and Bradford W. Mott
@version $Id: SoundSDL.hxx,v 1.7 2004-04-04 02:03:15 stephena Exp $
*/
class SoundSDL : public Sound
{
public:
/**
Create a new sound object
*/
SoundSDL(uInt32 fragsize, uInt32 queuesize);
/**
Destructor
*/
virtual ~SoundSDL();
public:
/**
Closes the sound device
*/
void closeDevice();
/**
Return the playback sample rate for the sound device.
@return The playback sample rate
*/
uInt32 getSampleRate() const;
/**
Return true iff the sound device was successfully initialized.
@return true iff the sound device was successfully initialized
*/
bool isSuccessfullyInitialized() const;
/**
Set the mute state of the sound object.
@param state Mutes sound if true, unmute if false
*/
void mute(bool state);
/**
Sets the volume of the sound device to the specified level. The
volume is given as a percentage from 0 to 100. A -1 indicates
that the volume shouldn't be changed at all.
@param percent The new volume percentage level for the sound device
*/
void setVolume(Int32 percent);
/**
Generates audio samples to fill the sample queue.
*/
void update();
/**
Sets the sound register to a given value.
@param addr The register address
@param value The value to save into the register
*/
void set(uInt16 addr, uInt8 value);
/**
Saves the current state of this device to the given Serializer.
@param out The serializer device to save to.
@return The result of the save. True on success, false on failure.
*/
bool save(Serializer& out);
/**
Loads the current state of this device from the given Deserializer.
@param in The deserializer device to load from.
@return The result of the load. True on success, false on failure.
*/
bool load(Deserializer& in);
private:
/**
A bounded queue class used to hold audio samples after they are
produced by the MediaSource.
*/
class SampleQueue
{
public:
/**
Create a new SampleQueue instance which can hold the specified
number of samples. If the queue ever reaches its capacity then
older samples are discarded.
*/
SampleQueue(uInt32 capacity);
/**
Destroy this SampleQueue instance.
*/
virtual ~SampleQueue();
public:
/**
Clear any samples stored in the queue.
*/
void clear();
/**
Dequeue the upto the specified number of samples and store them
in the buffer. Returns the actual number of samples removed from
the queue.
@return the actual number of samples removed from the queue.
*/
uInt32 dequeue(uInt8* buffer, uInt32 size);
/**
Enqueue the specified number of samples from the buffer.
*/
void enqueue(uInt8* buffer, uInt32 size);
/**
Answers the number of samples currently in the queue.
@return The number of samples in the queue.
*/
uInt32 size() const;
/**
Answers the maximum number of samples the queue can hold.
@return The maximum number of samples in the queue.
*/
uInt32 capacity() const { return myCapacity; }
private:
const uInt32 myCapacity;
uInt8* myBuffer;
uInt32 mySize;
uInt32 myHead;
uInt32 myTail;
};
private:
// Current volume
uInt32 myCurrentVolume;
// SDL fragment size
uInt32 myFragmentSize;
// Audio specification structure
SDL_AudioSpec myHardwareSpec;
// Indicates if the sound device was successfully initialized
bool myIsInitializedFlag;
// Indicates if the sound is currently muted
bool myIsMuted;
// DSP sample rate
uInt32 mySampleRate;
// The sample queue size (which is auto-adapting)
uInt32 mySampleQueueSize;
// Queue which holds samples from the media source before they are played
SampleQueue mySampleQueue;
private:
// Callback function invoked by the SDL Audio library when it needs data
static void callback(void* udata, uInt8* stream, int len);
};
#endif

File diff suppressed because it is too large Load Diff