Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,91 @@
TOOLPATH = /opt/mips64-toolchain
LIBN64PATH = $(TOOLPATH)/hkz-libn64/
GCCN64PREFIX = $(TOOLPATH)/bin/mips64-
srcdir = ../../..
VPATH = $(srcdir)
CC = $(GCCN64PREFIX)gcc
CXX = $(GCCN64PREFIX)g++
AS = $(GCCN64PREFIX)as
LD = $(GCCN64PREFIX)g++
OBJCOPY = $(GCCN64PREFIX)objcopy
AR = $(GCCN64PREFIX)ar cru
RANLIB = $(GCCN64PREFIX)ranlib
DEFINES += -D__N64__ -DLIMIT_FPS -DNONSTANDARD_PORT -DDISABLE_DEFAULT_SAVEFILEMANAGER -DDISABLE_TEXT_CONSOLE -DDISABLE_COMMAND_LINE -DDISABLE_FANCY_THEMES -DDISABLE_DOSBOX_OPL -DENABLE_VKEYBD -DUSE_ZLIB
LIBS += -lpakfs -lframfs -ln64 -ln64utils -lromfs
#DEFINES += -D_ENABLE_DEBUG_
USE_LIBMAD=0
USE_LIBOGG=1
ifeq ($(USE_LIBMAD),1)
DEFINES += -DUSE_MAD
LIBS += -lmad
endif
ifeq ($(USE_LIBOGG), 1)
DEFINES += -DUSE_VORBIS -DUSE_TREMOR
LIBS += -lvorbisidec
endif
LIBS += -lm -lstdc++ -lc -lgcc -lz -lnosys
CXXFLAGS = -g -mno-extern-sdata -O2 --param max-inline-insns-auto=20 -fomit-frame-pointer -march=vr4300 -mtune=vr4300 -mhard-float -fno-rtti -fno-exceptions -Wno-multichar -Wshadow -I$(LIBN64PATH) -I$(TOOLPATH)/include -I./ -I$(srcdir) -I$(srcdir)/engines
LDFLAGS = -g -march=vr4300 -mtune=vr4300 -nodefaultlibs -nostartfiles -mno-crt0 -L$(LIBN64PATH) -L$(TOOLPATH)/lib $(LIBS) -T n64ld_cpp.x -Xlinker -Map -Xlinker scummvm.map
TARGET = scummvm
DEPDIR = .deps
CXX_UPDATE_DEP_FLAG = -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d",-MQ,"$@",-MP
MKDIR = mkdir -p
RM = rm -f
RM_REC = rm -rf
VERBOSE_BUILD=1
USE_RGB_COLOR=0
ENABLED=STATIC_PLUGIN
#ENABLE_SCUMM = $(ENABLED)
#ENABLE_SCI = $(ENABLED)
#ENABLE_GOB = $(ENABLED)
#ENABLE_PARALLACTION = $(ENABLED)
#ENABLE_KYRA = $(ENABLED)
#ENABLE_AGOS = $(ENABLED)
#ENABLE_AGI = $(ENABLED)
#ENABLE_QUEEN = $(ENABLED)
#ENABLE_MADE = $(ENABLED)
#ENABLE_SAGA = $(ENABLED)
#ENABLE_TEENAGENT = $(ENABLED)
#ENABLE_DRACI = $(ENABLED)
OBJS := nintendo64.o osys_n64_base.o osys_n64_events.o osys_n64_utilities.o pakfs_save_manager.o framfs_save_manager.o
BACKEND := n64
include $(srcdir)/Makefile.common
MODULE_DIRS += ./
all: $(TARGET).v64
$(TARGET).v64: $(TARGET).bin ROMFS.img bootcode
cat bootcode $(TARGET).bin ROMFS.img > $(TARGET).v64
./pad_rom.sh $(TARGET).v64
ROMFS.img:
genromfs -f ./ROMFS.img -d ./ROMFS -V romtest
$(TARGET).elf: $(OBJS)
+$(LD) -o $(TARGET).elf $(OBJS) $(LDFLAGS)
$(TARGET).bin : $(TARGET).elf
$(OBJCOPY) $(TARGET).elf $(TARGET).bin -O binary
spotless : distclean
$(RM) *.bin *.elf *.v64 *.img *.bak *.tmp *.map
send: $(TARGET).v64
sudo ucon64 --xv64 $(TARGET).v64

View File

@@ -0,0 +1,115 @@
ScummVM-N64 README
==============================================================================
Requirements
============
- Nintendo64 (haha...)
- N64 Gamepad
- N64 Mouse (optional, gives better game experience)
- Controller PAK / FlashRAM cart (for saves)
- Flash Cart or copier (like Myth N64 or DoctorV64)
- N64 4mb Memory expansion (this is _almost_ a requirement)
Build cart images from sources
==============================
You can download hkz-libn64 sources from here: http://hkzlab.ipv7.net/consoles.html
hkz-libn64 is a library to control Nintendo64 hardware (es, video, audio, input, etc.).
* TODO *
Build cart images from binaries
===============================
ScummVM N64 cart images are composed by two parts: a binary with executable code
and a romfs image, containing game datafiles and other useful data.
Prebuilt binaries needs to be joined with a romfs image containing datafiles
from _YOUR_ games.
To generate a romfs image under linux/unix you need 'genromfs' tool from
http://romfs.sourceforge.net/, create a directory, copy game data inside,
and then generate the romfs image.
[EXAMPLE]
mkdir GAMEDATA
cp -a ../games/mygamedata ./GAMEDATA
genromfs -f ./ROMFS.img -d ./GAMEDATA -V romname
This creates a ROMFS image with 'mygamedata' directory inside.
Now you need to join this with the appropriate scummvm engine binary:
[EXAMPLE]
cat scummvm-bass.bin ROMFS.img > scummvm-bass.n64
Now the last step, you need to fix the checksum of the image, there are various
tools for this, i normally use ucon64.
[EXAMPLE]
ucon64 --n64 --chk scummvm-bass.n64
This gives you a n64 cart image you can use with your flashcart/copier to play
the game.
* NOTE *
Some copiers requires additional treatment of the cart image before uploading:
the DoctorV64 for example requires the image to be byte-swapped and padded to
the nearest 2mb multiple size. You can use ucon64 tool to perform these changes.
[EXAMPLE]
ucon64 --n64 --v64 --padn=16777216 scummvm-bass.n64
You are then ready to upload the image to the cart and play!
Saving & loading
================
ScummVM-N64 port supports two kind of saves: FlashRAM saves and controller pak saves.
If both are detected, FlashRAM is used.
WARNING!!! ScummVM-N64 currently _ERASES_ the data contained in your FlashRAM and
controller PAK without warning!!! Plug those in only if you want to use their space
for ScummVM saves.
* Controller PAK:
The PAK is a device plugged in the expansion port located underneath the gamepad, it
contains at most 32 kilobytes of data, which makes it very limited for ScummVM use (a
lot of games save size grows over 32kb when the game approaches the end). It contains
battery backed up ram, so savegames might disappear if the battery runs off.
* FlashRAM:
FlashRAM is contained in some game carts (like Command & Conquer), it keeps 128 kilobytes
of data at most, it's a little slower in saving than Controller PAK (it might show up as
a few seconds delay when saving in-game), it keeps enough data for most (all) games supported
by the port. Because of FlashRAM hardware (and the sake of simplicity) there is a top limit of
save files that can be created (8, no matter what the sizes are).
Controls
========
* Using a N64 Pad:
Left trigger - ESC
Right trigger - Virtual Keyboard
Analog - Mouse movement
D-pad - *SLOW* Mouse movement
Start - F5 / Main menu in some games
Z - Mouse button 1
B - Mouse button 2
A - '.' / Skip dialogues in some games
C buttons - Numeric keypad keys
* Using a N64 Mouse:
Used like a normal PC mouse.
Notes
=====
- If virtual keyboard doesn't show up, you need to make sure you included
'vkeybd_default.zip' in the root of your romfs image.
- In some games (mostly gob) cursor movement might be choppy, it's a known
problem and related on how N64 port manages screen updates.
** TODO **
==========
Write the rest of this README.

View File

@@ -0,0 +1,70 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <n64utils.h>
#include "framfs_save_manager.h"
bool fram_deleteSaveGame(const char *filename) {
int res = framfs_removeFile(filename);
return (res == 0);
}
uint32 InFRAMSave::read(void *buf, uint32 cnt) {
return framfs_read(buf, 1, cnt, fd);
}
bool InFRAMSave::seek(int64 offs, int whence) {
framfs_seek(fd, offs, whence);
return true;
}
bool InFRAMSave::skip(uint32 offset) {
framfs_seek(fd, offset, SEEK_CUR);
return true;
}
uint32 OutFRAMSave::write(const void *buf, uint32 cnt) {
return framfs_write(buf, 1, cnt, fd);
}
Common::StringArray FRAMSaveManager::listSavefiles(const Common::String &pattern) {
FRAMDIR *dirp = framfs_opendir();
framfs_dirent *dp;
Common::StringArray list;
Common::String *fname;
while ((dp = framfs_readdir(dirp)) != NULL) {
fname = new Common::String(dp->entryname);
if (fname->matchString(pattern, false, NULL))
list.push_back(dp->entryname);
delete fname;
free(dp);
}
framfs_closedir(dirp);
return list;
}

View File

@@ -0,0 +1,152 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef __FRAMFS_SAVE_MANAGER__
#define __FRAMFS_SAVE_MANAGER__
#include <common/savefile.h>
#include <common/compression/deflate.h>
#include <framfs.h> // N64 FramFS library
bool fram_deleteSaveGame(const char *filename);
class InFRAMSave : public Common::InSaveFile {
private:
FRAMFILE *fd;
uint32 read(void *buf, uint32 cnt) override;
bool skip(uint32 offset) override;
bool seek(int64 offs, int whence) override;
public:
InFRAMSave() : fd(NULL) { }
~InFRAMSave() {
if (fd != NULL)
framfs_close(fd);
}
bool eos() const override {
return framfs_eof(fd);
}
void clearErr() override {
framfs_clearerr(fd);
}
int64 pos() const override {
return framfs_tell(fd);
}
int64 size() const override {
return fd->size;
}
bool readSaveGame(const char *filename) {
fd = framfs_open(filename, "r");
return (fd != NULL);
}
};
class OutFRAMSave : public Common::WriteStream {
private:
FRAMFILE *fd;
public:
uint32 write(const void *buf, uint32 cnt);
virtual int64 pos() const {
return framfs_tell(fd);
}
OutFRAMSave(const char *_filename) : fd(NULL) {
fd = framfs_open(_filename, "w");
}
~OutFRAMSave() {
if (fd != NULL) {
finalize();
framfs_close(fd);
}
}
bool err() const {
if (fd)
return (framfs_error(fd) == 1);
else
return true;
}
void clearErr() {
framfs_clearerr(fd);
}
void finalize() {
framfs_flush(fd);
}
};
class FRAMSaveManager : public Common::SaveFileManager {
public:
void updateSavefilesList(Common::StringArray &lockedFiles) override {
// this method is used to lock saves while cloud syncing
// as there is no network on N64, this method wouldn't be used
// thus it's not implemtented
}
Common::InSaveFile *openRawFile(const Common::String &filename) override {
InFRAMSave *s = new InFRAMSave();
if (s->readSaveGame(filename.c_str())) {
return s;
} else {
delete s;
return 0;
}
}
Common::OutSaveFile *openForSaving(const Common::String &filename, bool compress = true) override {
OutFRAMSave *s = new OutFRAMSave(filename.c_str());
if (!s->err()) {
return new Common::OutSaveFile(compress ? Common::wrapCompressedWriteStream(s) : s);
} else {
delete s;
return 0;
}
}
Common::InSaveFile *openForLoading(const Common::String &filename) override {
InFRAMSave *s = new InFRAMSave();
if (s->readSaveGame(filename.c_str())) {
return Common::wrapCompressedReadStream(s);
} else {
delete s;
return 0;
}
}
bool removeSavefile(const Common::String &filename) override {
return ::fram_deleteSaveGame(filename.c_str());
}
Common::StringArray listSavefiles(const Common::String &pattern) override;
bool exists(const Common::String &filename) override {
return InFRAMSave().readSaveGame(filename.c_str());
}
};
#endif

View File

@@ -0,0 +1,14 @@
MODULE := backends/platform/n64
MODULE_OBJS := \
nintendo64.o \
osys_n64_base.o \
osys_n64_events.o \
osys_n64_utilities.o \
pakfs_save_manager.o \
framfs_save_manager.o
# We don't use rules.mk but rather manually update OBJS and MODULE_DIRS.
MODULE_OBJS := $(addprefix $(MODULE)/, $(MODULE_OBJS))
OBJS := $(MODULE_OBJS) $(OBJS)
MODULE_DIRS += $(sort $(dir $(MODULE_OBJS)))

View File

@@ -0,0 +1,33 @@
N64_EXE_STRIPPED := scummvm_stripped$(EXEEXT)
bundle_name = n64-dist/scummvm
BASESIZE = 2097152
all: $(N64_EXE_STRIPPED)
$(N64_EXE_STRIPPED): $(EXECUTABLE)
$(STRIP) $< -o $@
n64-distclean:
rm -rf $(bundle_name)
rm $(N64_EXE_STRIPPED)
n64-dist: all
$(MKDIR) $(bundle_name)
$(MKDIR) $(bundle_name)/romfs
ifdef DIST_FILES_ENGINEDATA
$(CP) $(DIST_FILES_ENGINEDATA) $(bundle_name)/romfs
endif
ifdef DIST_FILES_NETWORKING
$(CP) $(DIST_FILES_NETWORKING) $(bundle_name)/romfs
endif
ifdef DIST_FILES_VKEYBD
$(CP) $(DIST_FILES_VKEYBD) $(bundle_name)/romfs
endif
$(CP) $(DIST_FILES_DOCS) $(bundle_name)/
genromfs -f $(bundle_name)/romfs.img -d $(bundle_name)/romfs -V scummvmn64
mips64-objcopy $(EXECUTABLE) $(bundle_name)/scummvm.elf -O binary
cat $(N64SDK)/hkz-libn64/bootcode $(bundle_name)/scummvm.elf $(bundle_name)/romfs.img > scummvm.v64
$(srcdir)/backends/platform/n64/pad_rom.sh scummvm.v64
rm scummvm.bak
mv scummvm.v64 $(bundle_name)/scummvm.v64

View File

@@ -0,0 +1,32 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "osys_n64.h"
int main(void) {
g_system = new OSystem_N64();
assert(g_system);
// Invoke the actual ScummVM main entry point:
int res = scummvm_main(0, NULL);
g_system->quit(); // TODO: Consider removing / replacing this!
return res;
}

View File

@@ -0,0 +1,209 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef __OSYS_N64_H__
#define __OSYS_N64_H__
#include "common/rect.h"
#include "common/config-manager.h"
#include "backends/base-backend.h"
#include "base/main.h"
#include "graphics/surface.h"
#include "graphics/paletteman.h"
#include "graphics/pixelformat.h"
#include "audio/mixer_intern.h"
#include <libn64.h>
#include <n64utils.h>
#define DEFAULT_SOUND_SAMPLE_RATE 8000 // 8 kHz
//#define DEFAULT_SOUND_SAMPLE_RATE 11025 // 11 kHz
#define N64_PAL_FPS 25
#define N64_NTSC_FPS 30
typedef int (*TimerProc)(int interval);
// Interrupt callback functions
void vblCallback(void);
void sndCallback(void);
void refillAudioBuffers(void);
// External utility functions
void enableAudioPlayback(void);
void disableAudioPlayback(void);
void checkTimers(void);
int timer_handler(int t);
static volatile bool _audioEnabled = false; // Used by interrupt callbacks
/* Graphic mode identifiers */
enum GraphicModeID {
OVERS_NTSC_340X240,
NORM_NTSC_320X240,
NORM_PAL_320X240,
OVERS_PAL_340X240,
NORM_MPAL_320X240,
OVERS_MPAL_340X240
};
class OSystem_N64 : public EventsBaseBackend, public PaletteManager {
protected:
Audio::MixerImpl *_mixer;
struct display_context * _dc; // Display context for N64 on screen buffer switching
Graphics::Surface _framebuffer;
uint16 *_offscreen_hic; // Offscreen converted to 16bit surface
uint8 *_offscreen_pal; // Offscreen with palette indexes
uint16 *_overlayBuffer; // Offscreen for the overlay (16 bit)
uint16 *_screenPalette; // Array for palette entries (256 colors max)
#ifndef N64_EXTREME_MEMORY_SAVING
uint8 *_screenExactPalette; // Array for palette entries, as received by setPalette(), no precision loss
#endif
uint16 _cursorPalette[256]; // Palette entries for the cursor
int _graphicMode; // Graphic mode
uint16 _screenWidth, _screenHeight;
uint16 _gameWidth, _gameHeight;
uint16 _frameBufferWidth; // Width of framebuffer in N64 memory
uint8 _offscrPixels; // Pixels to skip on each line before start drawing, used to center image
uint8 _maxFps; // Max frames-per-second which can be shown on screen
int _shakeXOffset;
int _shakeYOffset;
uint8 *_cursor_pal; // Cursor buffer, palettized
uint16 *_cursor_hic; // Cursor buffer, 16bit
bool _cursorPaletteDisabled;
bool _dirtyPalette;
uint _cursorWidth, _cursorHeight;
int _cursorKeycolor;
uint16 _overlayHeight, _overlayWidth;
bool _overlayVisible;
bool _overlayInGUI;
bool _disableFpsLimit; // When this is enabled, the system doesn't limit screen updates
bool _mouseVisible;
volatile int _mouseX, _mouseY;
volatile float _tempMouseX, _tempMouseY;
volatile int _mouseMaxX, _mouseMaxY;
int _mouseHotspotX, _mouseHotspotY;
int8 _controllerPort;
int8 _mousePort;
bool _controllerHasRumble; // Gets enabled if rumble-pak is detected
bool _dirtyOffscreen;
public:
/* These have to be accessed by interrupt callbacks */
uint16 _audioBufferSize;
uint32 _viClockRate; // Clock rate of video system, depending on VI mode
uint32 _timerCallbackNext;
uint32 _timerCallbackTimer;
TimerProc _timerCallback;
/* *** */
OSystem_N64();
virtual ~OSystem_N64();
virtual void initBackend();
virtual bool hasFeature(Feature f);
virtual void setFeatureState(Feature f, bool enable);
virtual bool getFeatureState(Feature f);
virtual const GraphicsMode *getSupportedGraphicsModes() const;
virtual int getDefaultGraphicsMode() const;
virtual bool setGraphicsMode(int mode, uint flags = OSystem::kGfxModeNoFlags);
virtual int getGraphicsMode() const;
virtual void initSize(uint width, uint height, const Graphics::PixelFormat *format);
virtual int16 getHeight();
virtual int16 getWidth();
virtual PaletteManager *getPaletteManager() { return this; }
protected:
// PaletteManager API
virtual void setPalette(const byte *colors, uint start, uint num);
virtual void grabPalette(byte *colors, uint start, uint num) const;
public:
virtual void copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h);
virtual void updateScreen();
virtual Graphics::Surface *lockScreen();
virtual void unlockScreen();
virtual void setShakePos(int shakeXOffset, int shakeYOffset);
virtual void showOverlay(bool inGUI);
virtual void hideOverlay();
virtual bool isOverlayVisible() const { return _overlayVisible; }
virtual void clearOverlay();
virtual void grabOverlay(Graphics::Surface &surface);
virtual void copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h);
virtual int16 getOverlayHeight() const;
virtual int16 getOverlayWidth() const;
virtual Graphics::PixelFormat getOverlayFormat() const {
return Graphics::PixelFormat(2, 5, 5, 5, 0, 11, 6, 1, 0);
}
virtual bool showMouse(bool visible);
virtual void warpMouse(int x, int y);
virtual void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format, const byte *mask);
virtual void setCursorPalette(const byte *colors, uint start, uint num);
virtual bool pollEvent(Common::Event &event);
virtual uint32 getMillis(bool skipRecord = false);
virtual void delayMillis(uint msecs);
virtual Common::MutexInternal *createMutex(void);
virtual void quit();
virtual Audio::Mixer *getMixer();
virtual void getTimeAndDate(TimeDate &t, bool skipRecord = false) const;
virtual void setTimerCallback(TimerProc callback, int interval);
virtual void logMessage(LogMessageType::Type type, const char *message);
void rebuildOffscreenGameBuffer(void);
void rebuildOffscreenMouseBuffer(void);
void switchGraphicModeId(int mode);
void setupMixer(void);
void detectControllers(void);
void readControllerAnalogInput(void); // read controller analog nub position
};
#endif /* __OSYS_N64_H__ */

View File

@@ -0,0 +1,909 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#define FORBIDDEN_SYMBOL_ALLOW_ALL
#include <romfs.h>
#include <malloc.h> // Required for memalign
#include "osys_n64.h"
#include "pakfs_save_manager.h"
#include "framfs_save_manager.h"
#include "backends/fs/n64/n64-fs-factory.h"
#include "backends/mutex/null/null-mutex.h"
#include "backends/saves/default/default-saves.h"
#include "backends/timer/default/default-timer.h"
#include "graphics/blit.h"
typedef unsigned long long uint64;
extern uint8 _romfs; // Defined by linker (used to calculate position of romfs image)
inline uint16 colRGB888toBGR555(byte r, byte g, byte b);
static const OSystem::GraphicsMode s_supportedGraphicsModes[] = {
{ "320x240 (PAL) fix overscan", "340x240 PAL", OVERS_PAL_340X240 },
{ "320x240 (PAL) overscan", "320x240 PAL", NORM_PAL_320X240 },
{ "320x240 (MPAL) fix overscan", "340x240 MPAL", OVERS_MPAL_340X240 },
{ "320x240 (MPAL) overscan", "320x240 MPAL", NORM_MPAL_320X240 },
{ "340x240 (NTSC) fix overscan", "340x240 NTSC", OVERS_NTSC_340X240 },
{ "320x240 (NTSC) overscan", "320x240 NTSC", NORM_NTSC_320X240 },
{ 0, 0, 0 }
};
OSystem_N64::OSystem_N64() {
// Enable Mips interrupts
set_MI_interrupt(1);
// Initialize display: NTSC 340x240 (16 bit)
initDisplay(NTSC_340X240_16BIT);
// Prepare virtual text layer for debugging purposes
initTextLayer();
// Init PI interface
PI_Init();
// Screen size
_screenWidth = 320;
_screenHeight = 240;
// Game screen size
_gameHeight = 320;
_gameWidth = 240;
// Overlay size
_overlayWidth = 320;
_overlayHeight = 240;
// Framebuffer width
_frameBufferWidth = 340;
// Pixels to skip
_offscrPixels = 16;
// Video clock
_viClockRate = VI_NTSC_CLOCK;
// Max FPS
_maxFps = N64_NTSC_FPS;
_disableFpsLimit = false;
_overlayVisible = false;
_overlayInGUI = false;
_shakeXOffset = 0;
_shakeYOffset = 0;
// Allocate memory for offscreen buffers
_offscreen_hic = (uint16 *)memalign(8, _screenWidth * _screenHeight * 2);
_offscreen_pal = (uint8 *)memalign(8, _screenWidth * _screenHeight);
_overlayBuffer = (uint16 *)memalign(8, _overlayWidth * _overlayHeight * sizeof(uint16));
_cursor_pal = NULL;
_cursor_hic = NULL;
_cursorWidth = 0;
_cursorHeight = 0;
_cursorKeycolor = -1;
_mouseHotspotX = _mouseHotspotY = -1;
// Clean offscreen buffers
memset(_offscreen_hic, 0, _screenWidth * _screenHeight * 2);
memset(_offscreen_pal, 0, _screenWidth * _screenHeight);
memset(_overlayBuffer, 0, _overlayWidth * _overlayHeight * sizeof(uint16));
// Default graphic mode
_graphicMode = OVERS_NTSC_340X240;
// Clear palette array
_screenPalette = (uint16 *)memalign(8, 256 * sizeof(uint16));
#ifndef N64_EXTREME_MEMORY_SAVING
_screenExactPalette = (uint8 *)memalign(8, 256 * 3);
memset(_screenExactPalette, 0, 256 * 3);
#endif
memset(_screenPalette, 0, 256 * sizeof(uint16));
memset(_cursorPalette, 0, 256 * sizeof(uint16));
_dirtyPalette = false;
_cursorPaletteDisabled = false;
_audioEnabled = false;
// Initialize ROMFS access interface
initRomFSmanager((uint8 *)(((uint32)&_romfs + (uint32)0xc00) | (uint32)0xB0000000));
_mouseVisible = false;
_mouseX = _overlayWidth / 2;
_mouseY = _overlayHeight / 2;
_tempMouseX = _mouseX;
_tempMouseY = _mouseY;
_mouseMaxX = _overlayWidth;
_mouseMaxY = _overlayHeight;
_mixer = 0;
_dirtyOffscreen = false;
detectControllers();
_controllerHasRumble = (identifyPak(_controllerPort) == 2);
_fsFactory = new N64FilesystemFactory();
// Register vblank callback (this MUST be done at the END of init).
registerVIhandler(vblCallback);
}
OSystem_N64::~OSystem_N64() {
delete _mixer;
}
void OSystem_N64::initBackend() {
ConfMan.setInt("autosave_period", 0);
ConfMan.setBool("FM_high_quality", false);
ConfMan.setBool("FM_medium_quality", true);
ConfMan.set("gui_theme", "modern"); // In case of modern theme being present, use it.
FRAM_Init();
if (FRAM_Detect()) { // Use FlashRAM
initFramFS();
_savefileManager = new FRAMSaveManager();
} else { // Use PakFS
// Init Controller Pak
initPakFs();
// Use the first controller pak found
uint8 ctrl_num;
for (ctrl_num = 0; ctrl_num < 4; ctrl_num++) {
int8 pak_type = identifyPak(ctrl_num);
if (pak_type == 1) {
loadPakData(ctrl_num);
break;
}
}
_savefileManager = new PAKSaveManager();
}
_timerManager = new DefaultTimerManager();
setTimerCallback(&timer_handler, 10);
setupMixer();
EventsBaseBackend::initBackend();
}
bool OSystem_N64::hasFeature(Feature f) {
return (f == kFeatureCursorPalette);
}
void OSystem_N64::setFeatureState(Feature f, bool enable) {
if (f == kFeatureCursorPalette) {
_cursorPaletteDisabled = !enable;
// Rebuild cursor hicolor buffer
rebuildOffscreenMouseBuffer();
_dirtyOffscreen = true;
}
}
bool OSystem_N64::getFeatureState(Feature f) {
if (f == kFeatureCursorPalette)
return !_cursorPaletteDisabled;
return false;
}
const OSystem::GraphicsMode* OSystem_N64::getSupportedGraphicsModes() const {
return s_supportedGraphicsModes;
}
int OSystem_N64::getDefaultGraphicsMode() const {
return OVERS_NTSC_340X240;
}
bool OSystem_N64::setGraphicsMode(int mode, uint /*flags*/) {
_graphicMode = mode;
switchGraphicModeId(_graphicMode);
return true;
}
void OSystem_N64::switchGraphicModeId(int mode) {
switch (mode) {
case NORM_PAL_320X240:
disableAudioPlayback();
_viClockRate = VI_PAL_CLOCK;
_maxFps = N64_PAL_FPS;
initDisplay(PAL_320X240_16BIT);
_frameBufferWidth = 320;
_screenWidth = 320;
_screenHeight = 240;
_offscrPixels = 0;
_graphicMode = NORM_PAL_320X240;
enableAudioPlayback();
break;
case OVERS_PAL_340X240:
disableAudioPlayback();
_viClockRate = VI_PAL_CLOCK;
_maxFps = N64_PAL_FPS;
initDisplay(PAL_340X240_16BIT);
_frameBufferWidth = 340;
_screenWidth = 320;
_screenHeight = 240;
_offscrPixels = 16;
_graphicMode = OVERS_PAL_340X240;
enableAudioPlayback();
break;
case NORM_MPAL_320X240:
disableAudioPlayback();
_viClockRate = VI_MPAL_CLOCK;
_maxFps = N64_NTSC_FPS;
initDisplay(MPAL_320X240_16BIT);
_frameBufferWidth = 320;
_screenWidth = 320;
_screenHeight = 240;
_offscrPixels = 0;
_graphicMode = NORM_MPAL_320X240;
enableAudioPlayback();
break;
case OVERS_MPAL_340X240:
disableAudioPlayback();
_viClockRate = VI_MPAL_CLOCK;
_maxFps = N64_NTSC_FPS;
initDisplay(MPAL_340X240_16BIT);
_frameBufferWidth = 340;
_screenWidth = 320;
_screenHeight = 240;
_offscrPixels = 16;
_graphicMode = OVERS_MPAL_340X240;
enableAudioPlayback();
break;
case NORM_NTSC_320X240:
disableAudioPlayback();
_viClockRate = VI_NTSC_CLOCK;
_maxFps = N64_NTSC_FPS;
initDisplay(NTSC_320X240_16BIT);
_frameBufferWidth = 320;
_screenWidth = 320;
_screenHeight = 240;
_offscrPixels = 0;
_graphicMode = NORM_NTSC_320X240;
enableAudioPlayback();
break;
case OVERS_NTSC_340X240:
default:
disableAudioPlayback();
_viClockRate = VI_NTSC_CLOCK;
_maxFps = N64_NTSC_FPS;
initDisplay(NTSC_340X240_16BIT);
_frameBufferWidth = 340;
_screenWidth = 320;
_screenHeight = 240;
_offscrPixels = 16;
_graphicMode = OVERS_NTSC_340X240;
enableAudioPlayback();
break;
}
}
int OSystem_N64::getGraphicsMode() const {
return _graphicMode;
}
void OSystem_N64::initSize(uint width, uint height, const Graphics::PixelFormat *format) {
_gameWidth = width;
_gameHeight = height;
if (_gameWidth > _screenWidth)
_gameWidth = _screenWidth;
if (_gameHeight > _screenHeight)
_gameHeight = _screenHeight;
_mouseMaxX = _gameWidth;
_mouseMaxY = _gameHeight;
}
int16 OSystem_N64::getHeight() {
return _screenHeight;
}
int16 OSystem_N64::getWidth() {
return _screenWidth;
}
void OSystem_N64::setPalette(const byte *colors, uint start, uint num) {
#ifndef N64_EXTREME_MEMORY_SAVING
memcpy(_screenExactPalette + start * 3, colors, num * 3);
#endif
for (uint i = 0; i < num; ++i) {
_screenPalette[start + i] = colRGB888toBGR555(colors[2], colors[1], colors[0]);
colors += 3;
}
// If cursor uses the game palette, we need to rebuild the hicolor buffer
if (_cursorPaletteDisabled)
rebuildOffscreenMouseBuffer();
_dirtyPalette = true;
_dirtyOffscreen = true;
}
void OSystem_N64::rebuildOffscreenGameBuffer(void) {
// Regenerate hi-color offscreen buffer
uint64 four_col_hi;
uint32 four_col_pal;
for (int h = 0; h < _gameHeight; h++) {
for (int w = 0; w < _gameWidth; w += 4) {
four_col_pal = *(uint32 *)(_offscreen_pal + ((h * _screenWidth) + w));
four_col_hi = 0;
four_col_hi |= (uint64)_screenPalette[((four_col_pal >> 24) & 0xFF)] << 48;
four_col_hi |= (uint64)_screenPalette[((four_col_pal >> 16) & 0xFF)] << 32;
four_col_hi |= (uint64)_screenPalette[((four_col_pal >> 8) & 0xFF)] << 16;
four_col_hi |= (uint64)_screenPalette[((four_col_pal >> 0) & 0xFF)] << 0;
// Save the converted pixels into hicolor buffer
*(uint64 *)((_offscreen_hic) + (h * _screenWidth) + w) = four_col_hi;
}
}
}
void OSystem_N64::rebuildOffscreenMouseBuffer(void) {
uint16 width, height;
uint16 *_pal_src = _cursorPaletteDisabled ? _screenPalette : _cursorPalette;
for (height = 0; height < _cursorHeight; height++) {
for (width = 0; width < _cursorWidth; width++) {
uint8 pix = _cursor_pal[(_cursorWidth * height) + width];
// Enable alpha bit in cursor buffer if pixel should be invisible
_cursor_hic[(_cursorWidth * height) + width] = (pix != _cursorKeycolor) ? _pal_src[pix] : 0x0001;
}
}
}
void OSystem_N64::grabPalette(byte *colors, uint start, uint num) const {
#ifdef N64_EXTREME_MEMORY_SAVING // This way loses precisions
uint32 i;
uint16 color;
for (i = start; i < start + num; i++) {
color = _screenPalette[i];
// Color format on the n64 is RGB - 1555
*colors++ = ((color & 0x1F) << 3);
*colors++ = (((color >> 5) & 0x1F) << 3);
*colors++ = (((color >> 10) & 0x1F) << 3);
}
#else
memcpy(colors, _screenExactPalette + start * 3, num * 3);
#endif
return;
}
void OSystem_N64::setCursorPalette(const byte *colors, uint start, uint num) {
for (uint i = 0; i < num; ++i) {
_cursorPalette[start + i] = colRGB888toBGR555(colors[2], colors[1], colors[0]);
colors += 3;
}
_cursorPaletteDisabled = false;
// Rebuild cursor hicolor buffer
rebuildOffscreenMouseBuffer();
_dirtyOffscreen = true;
}
void OSystem_N64::copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h) {
//Clip the coordinates
const byte *src = (const byte *)buf;
if (x < 0) {
w += x;
src -= x;
x = 0;
}
if (y < 0) {
h += y;
src -= y * pitch;
y = 0;
}
if (w > _screenWidth - x) {
w = _screenWidth - x;
}
if (h > _screenHeight - y) {
h = _screenHeight - y;
}
if (w <= 0 || h <= 0)
return;
uint8 *dst_pal = _offscreen_pal + ((y * _screenWidth) + x);
uint16 *dst_hicol = _offscreen_hic + ((y * _screenWidth) + x);
do {
for (int hor = 0; hor < w; hor++) {
if (dst_pal[hor] != src[hor]) {
uint16 color = _screenPalette[src[hor]];
dst_hicol[hor] = color; // Save image converted to 16-bit
dst_pal[hor] = src[hor]; // Save palettized display
}
}
src += pitch;
dst_pal += _screenWidth;
dst_hicol += _screenWidth;
} while (--h);
_dirtyOffscreen = true;
return;
}
void OSystem_N64::updateScreen() {
#ifdef LIMIT_FPS
static uint32 _lastScreenUpdate = 0;
if (!_disableFpsLimit) {
uint32 now = getMillis();
if (now - _lastScreenUpdate < 1000 / _maxFps)
return;
_lastScreenUpdate = now;
}
#endif
// Check if audio buffer needs refill
// Done here because this gets called regularly
refillAudioBuffers();
if (!_dirtyOffscreen && !_dirtyPalette) return; // The offscreen is clean
uint8 skip_lines = (_screenHeight - _gameHeight) / 4;
uint8 skip_pixels = (_screenWidth - _gameWidth) / 2; // Center horizontally the image
skip_pixels -= (skip_pixels % 8); // To keep aligned memory access
if (_dirtyPalette)
rebuildOffscreenGameBuffer();
// Obtain the framebuffer
while (!(_dc = lockDisplay()));
uint16 *overlay_framebuffer = (uint16 *)_dc->conf.framebuffer; // Current screen framebuffer
uint16 *game_framebuffer = overlay_framebuffer + (_frameBufferWidth * skip_lines * 2); // Skip some lines to center the image vertically
uint16 currentHeight, currentWidth;
uint16 *tmpDst;
uint16 *tmpSrc;
// Copy the game buffer to screen
if (!_overlayVisible) {
tmpDst = game_framebuffer;
tmpSrc = _offscreen_hic + (_shakeYOffset * _screenWidth);
for (currentHeight = _shakeYOffset; currentHeight < _gameHeight; currentHeight++) {
uint64 *game_dest = (uint64 *)(tmpDst + skip_pixels + _offscrPixels);
uint64 *game_src = (uint64 *)tmpSrc;
// With uint64 we copy 4 pixels at a time
for (currentWidth = 0; currentWidth < _gameWidth; currentWidth += 4) {
*game_dest++ = *game_src++;
}
tmpDst += _frameBufferWidth;
tmpSrc += _screenWidth;
}
uint16 _clearLines = _shakeYOffset; // When shaking we must take care of remaining lines to clear
while (_clearLines--) {
memset(tmpDst + skip_pixels + _offscrPixels, 0, _screenWidth * 2);
tmpDst += _frameBufferWidth;
}
} else { // If the overlay is enabled, draw it on top of game screen
tmpDst = overlay_framebuffer;
tmpSrc = _overlayBuffer;
for (currentHeight = 0; currentHeight < _overlayHeight; currentHeight++) {
uint64 *over_dest = (uint64 *)(tmpDst + _offscrPixels);
uint64 *over_src = (uint64 *)tmpSrc;
// Copy 4 pixels at a time
for (currentWidth = 0; currentWidth < _overlayWidth; currentWidth += 4) {
*over_dest++ = *over_src++;
}
tmpDst += _frameBufferWidth;
tmpSrc += _overlayWidth;
}
}
// Draw mouse cursor
if ((_mouseVisible || _overlayVisible) && _cursorHeight > 0 && _cursorWidth > 0) {
uint16 *mouse_framebuffer;
uint16 horiz_pix_skip;
if (_overlayVisible) {
mouse_framebuffer = overlay_framebuffer;
horiz_pix_skip = 0;
} else {
mouse_framebuffer = game_framebuffer;
horiz_pix_skip = skip_pixels;
}
for (uint h = 0; h < _cursorHeight; h++) {
for (uint w = 0; w < _cursorWidth; w++) {
int posX = (_mouseX - _mouseHotspotX) + w;
int posY = (_mouseY - _mouseHotspotY) + h;
// Draw pixel
if ((posY >= 0) && (posY < _mouseMaxY) && (posX >= 0) && (posX < _mouseMaxX)) {
uint16 cursor_pixel_hic = _cursor_hic[(h * _cursorWidth) + w];
if (!(cursor_pixel_hic & 0x00001))
mouse_framebuffer[(posY * _frameBufferWidth) + (posX + _offscrPixels + horiz_pix_skip)] = cursor_pixel_hic;
}
}
}
}
#ifndef _ENABLE_DEBUG_
showDisplay(_dc);
#else
showDisplayAndText(_dc);
#endif
_dc = NULL;
_dirtyOffscreen = false;
_dirtyPalette = false;
return;
}
Graphics::Surface *OSystem_N64::lockScreen() {
_framebuffer.init(_gameWidth, _gameHeight, _screenWidth, _offscreen_pal, Graphics::PixelFormat::createFormatCLUT8());
return &_framebuffer;
}
void OSystem_N64::unlockScreen() {
_dirtyPalette = true;
_dirtyOffscreen = true;
}
void OSystem_N64::setShakePos(int shakeXOffset, int shakeYOffset) {
// If a rumble pak is plugged in and screen shakes, rumble!
if (shakeYOffset && _controllerHasRumble) rumblePakEnable(1, _controllerPort);
else if (!shakeYOffset && _controllerHasRumble) rumblePakEnable(0, _controllerPort);
_shakeXOffset = shakeXOffset;
_shakeYOffset = shakeYOffset;
_dirtyOffscreen = true;
return;
}
void OSystem_N64::showOverlay(bool inGUI) {
_overlayInGUI = inGUI;
if (inGUI) {
// Change min/max mouse coords
_mouseMaxX = _overlayWidth;
_mouseMaxY = _overlayHeight;
// Relocate the mouse cursor given the new limitations
warpMouse(_mouseX, _mouseY);
}
_overlayVisible = true;
_dirtyOffscreen = true;
}
void OSystem_N64::hideOverlay() {
if (_overlayInGUI) {
// Change min/max mouse coords
_mouseMaxX = _gameWidth;
_mouseMaxY = _gameHeight;
// Relocate the mouse cursor given the new limitations
warpMouse(_mouseX, _mouseY);
}
_overlayVisible = false;
_overlayInGUI = false;
// Clear double buffered display
clearAllVideoBuffers();
_dirtyOffscreen = true;
// Force TWO screen updates (because of double buffered display).
// This way games which won't automatically update the screen
// when overlay is disabled, won't show a black screen. (eg. Lure)
_disableFpsLimit = true;
updateScreen();
updateScreen();
_disableFpsLimit = false;
}
void OSystem_N64::clearOverlay() {
memset(_overlayBuffer, 0, _overlayWidth * _overlayHeight * sizeof(uint16));
uint8 skip_lines = (_screenHeight - _gameHeight) / 4;
uint8 skip_pixels = (_screenWidth - _gameWidth) / 2; // Center horizontally the image
uint16 *tmpDst = _overlayBuffer + (_overlayWidth * skip_lines * 2);
uint16 *tmpSrc = _offscreen_hic + (_shakeYOffset * _screenWidth);
for (uint16 currentHeight = _shakeYOffset; currentHeight < _gameHeight; currentHeight++) {
memcpy((tmpDst + skip_pixels), tmpSrc, _gameWidth * 2);
tmpDst += _overlayWidth;
tmpSrc += _screenWidth;
}
_dirtyOffscreen = true;
}
void OSystem_N64::grabOverlay(Graphics::Surface &surface) {
assert(surface.w >= _overlayWidth);
assert(surface.h >= _overlayHeight);
assert(surface.format.bytesPerPixel == sizeof(uint16));
byte *src = (byte *)_overlayBuffer;
byte *dst = (byte *)surface.getPixels();
Graphics::copyBlit(dst, src, surface.pitch, _overlayWidth * sizeof(uint16),
_overlayWidth, _overlayHeight, sizeof(uint16));
}
void OSystem_N64::copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h) {
const byte *src = (const byte *)buf;
//Clip the coordinates
if (x < 0) {
w += x;
src -= x * sizeof(uint16);
x = 0;
}
if (y < 0) {
h += y;
src -= y * pitch;
y = 0;
}
if (w > _overlayWidth - x) {
w = _overlayWidth - x;
}
if (h > _overlayHeight - y) {
h = _overlayHeight - y;
}
if (w <= 0 || h <= 0)
return;
uint16 *dst = _overlayBuffer + (y * _overlayWidth + x);
if (_overlayWidth == (uint16)w && (uint16)pitch == _overlayWidth * sizeof(uint16)) {
memcpy(dst, src, h * pitch);
} else {
do {
memcpy(dst, src, w * sizeof(uint16));
src += pitch;
dst += _overlayWidth;
} while (--h);
}
_dirtyOffscreen = true;
return;
}
int16 OSystem_N64::getOverlayHeight() const {
return _overlayHeight;
}
int16 OSystem_N64::getOverlayWidth() const {
return _overlayWidth;
}
bool OSystem_N64::showMouse(bool visible) {
bool last = _mouseVisible;
_mouseVisible = visible;
_dirtyOffscreen = true;
return last;
}
void OSystem_N64::warpMouse(int x, int y) {
if (x < 0)
_mouseX = 0;
else if (x >= _mouseMaxX)
_mouseX = _mouseMaxX - 1;
else
_mouseX = x;
if (y < 0)
_mouseY = 0;
else if (y >= _mouseMaxY)
_mouseY = _mouseMaxY - 1;
else
_mouseY = y;
_dirtyOffscreen = true;
}
void OSystem_N64::setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format, const byte *mask) {
if (!w || !h) return;
if (mask)
warning("OSystem_N64::setMouseCursor: Masks are not supported");
_mouseHotspotX = hotspotX;
_mouseHotspotY = hotspotY;
if (_cursor_pal && ((w != _cursorWidth) || (h != _cursorHeight))) {
free(_cursor_pal);
free(_cursor_hic);
_cursor_pal = NULL;
_cursor_hic = NULL;
}
if (!_cursor_pal) {
_cursor_pal = (uint8 *)malloc(w * h);
_cursor_hic = (uint16 *)malloc(w * h * sizeof(uint16));
}
_cursorWidth = w;
_cursorHeight = h;
memcpy(_cursor_pal, buf, w * h); // Copy the palettized cursor
_cursorKeycolor = keycolor & 0xFF;
// Regenerate cursor hicolor buffer
rebuildOffscreenMouseBuffer();
_dirtyOffscreen = true;
return;
}
uint32 OSystem_N64::getMillis(bool skipRecord) {
return getMilliTick();
}
void OSystem_N64::delayMillis(uint msecs) {
// In some cases a game might hang waiting for audio being
// played. This is a workaround for all the situations i
// found (kyra 1 & 2 DOS).
uint32 oldTime = getMilliTick();
refillAudioBuffers();
uint32 pastMillis = (getMilliTick() - oldTime);
if (pastMillis >= msecs) {
return;
} else {
delay(msecs - pastMillis);
}
}
// As we don't have multi-threading, no need for mutexes
Common::MutexInternal *OSystem_N64::createMutex(void) {
return new NullMutexInternal();
}
void OSystem_N64::quit() {
// Not much to do...
return;
}
Audio::Mixer *OSystem_N64::getMixer() {
assert(_mixer);
return _mixer;
}
void OSystem_N64::getTimeAndDate(TimeDate &t, bool skipRecord) const {
// No RTC inside the N64, read mips timer to simulate
// passing of time, not a perfect solution, but can't think
// of anything better.
uint32 now = getMilliTick();
t.tm_sec = (now / 1000) % 60;
t.tm_min = ((now / 1000) / 60) % 60;
t.tm_hour = (((now / 1000) / 60) / 60) % 24;
t.tm_mday = 1;
t.tm_mon = 0;
t.tm_year = 110;
t.tm_wday = 0;
return;
}
void OSystem_N64::logMessage(LogMessageType::Type type, const char *message) {
FILE *output = 0;
if (type == LogMessageType::kInfo || type == LogMessageType::kDebug)
output = stdout;
else
output = stderr;
fputs(message, output);
fflush(output);
}
void OSystem_N64::setTimerCallback(TimerProc callback, int interval) {
assert (interval > 0);
if (callback != NULL) {
_timerCallbackTimer = interval;
_timerCallbackNext = getMillis() + interval;
_timerCallback = callback;
} else
_timerCallback = NULL;
}
void OSystem_N64::setupMixer(void) {
_mixer = new Audio::MixerImpl(DEFAULT_SOUND_SAMPLE_RATE);
_mixer->setReady(false);
enableAudioPlayback();
}
/* Check all controller ports for a compatible input adapter. */
void OSystem_N64::detectControllers(void) {
controller_data_status *ctrl_status = (controller_data_status *)memalign(8, sizeof(controller_data_status));
controller_Read_Status(ctrl_status);
_controllerPort = -1; // Default no controller
_mousePort = -1; // Default no mouse
for (int8 ctrl_port = 3; ctrl_port >= 0; ctrl_port--) {
// Found a standard pad, use this by default.
if (ctrl_status->c[ctrl_port].type == CTRL_PAD_STANDARD) {
_controllerPort = ctrl_port;
} else if (ctrl_status->c[ctrl_port].type == CTRL_N64_MOUSE) {
_mousePort = ctrl_port;
}
}
free(ctrl_status);
}
inline uint16 colRGB888toBGR555(byte r, byte g, byte b) {
return ((r >> 3) << 1) | ((g >> 3) << 6) | ((b >> 3) << 11);
}

View File

@@ -0,0 +1,440 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <math.h> // Needed for "tan()" function
#include "osys_n64.h"
// Pad buttons
#define START_BUTTON(a) (a & 0x1000)
#define A_BUTTON(a) (a & 0x8000)
#define B_BUTTON(a) (a & 0x4000)
#define Z_BUTTON(a) (a & 0x2000)
// Triggers
#define TL_BUTTON(a) (a & 0x0020)
#define TR_BUTTON(a) (a & 0x0010)
// D-Pad
#define DL_BUTTON(a) (a & 0x0200)
#define DR_BUTTON(a) (a & 0x0100)
#define DU_BUTTON(a) (a & 0x0800)
#define DD_BUTTON(a) (a & 0x0400)
// Yellow C buttons
#define CL_BUTTON(a) (a & 0x0002)
#define CR_BUTTON(a) (a & 0x0001)
#define CU_BUTTON(a) (a & 0x0008)
#define CD_BUTTON(a) (a & 0x0004)
// Macro for button press checking
#define PRESSED_START(now, before) (START_BUTTON(now) && !START_BUTTON(before))
#define RELEASED_START(now, before) (!START_BUTTON(now) && START_BUTTON(before))
#define PRESSED_A(now, before) (A_BUTTON(now) && !A_BUTTON(before))
#define RELEASED_A(now, before) (!A_BUTTON(now) && A_BUTTON(before))
#define PRESSED_B(now, before) (B_BUTTON(now) && !B_BUTTON(before))
#define RELEASED_B(now, before) (!B_BUTTON(now) && B_BUTTON(before))
#define PRESSED_Z(now, before) (Z_BUTTON(now) && !Z_BUTTON(before))
#define RELEASED_Z(now, before) (!Z_BUTTON(now) && Z_BUTTON(before))
#define PRESSED_TL(now, before) (TL_BUTTON(now) && !TL_BUTTON(before))
#define RELEASED_TL(now, before) (!TL_BUTTON(now) && TL_BUTTON(before))
#define PRESSED_TR(now, before) (TR_BUTTON(now) && !TR_BUTTON(before))
#define RELEASED_TR(now, before) (!TR_BUTTON(now) && TR_BUTTON(before))
#define PRESSED_DL(now, before) (DL_BUTTON(now) && !DL_BUTTON(before))
#define RELEASED_DL(now, before) (!DL_BUTTON(now) && DL_BUTTON(before))
#define PRESSED_DR(now, before) (DR_BUTTON(now) && !DR_BUTTON(before))
#define RELEASED_DR(now, before) (!DR_BUTTON(now) && DR_BUTTON(before))
#define PRESSED_DU(now, before) (DU_BUTTON(now) && !DU_BUTTON(before))
#define RELEASED_DU(now, before) (!DU_BUTTON(now) && DU_BUTTON(before))
#define PRESSED_DD(now, before) (DD_BUTTON(now) && !DD_BUTTON(before))
#define RELEASED_DD(now, before) (!DD_BUTTON(now) && DD_BUTTON(before))
#define PRESSED_CL(now, before) (CL_BUTTON(now) && !CL_BUTTON(before))
#define RELEASED_CL(now, before) (!CL_BUTTON(now) && CL_BUTTON(before))
#define PRESSED_CR(now, before) (CR_BUTTON(now) && !CR_BUTTON(before))
#define RELEASED_CR(now, before) (!CR_BUTTON(now) && CR_BUTTON(before))
#define PRESSED_CU(now, before) (CU_BUTTON(now) && !CU_BUTTON(before))
#define RELEASED_CU(now, before) (!CU_BUTTON(now) && CU_BUTTON(before))
#define PRESSED_CD(now, before) (CD_BUTTON(now) && !CD_BUTTON(before))
#define RELEASED_CD(now, before) (!CD_BUTTON(now) && CD_BUTTON(before))
#define MOUSE_DEADZONE 0
#define MOUSE_SENSIBILITY 1.5f
#define PAD_DEADZONE 1
#define PAD_ACCELERATION 15
#define PAD_CHECK_TIME 40
static controller_data_buttons _ctrlData;
void OSystem_N64::readControllerAnalogInput(void) {
int8 pad_analogX, pad_analogY;
int8 pad_mouseX, pad_mouseY;
// Read current controller status
controller_Read_Buttons(&_ctrlData);
pad_analogX = 0;
pad_analogY = 0;
if (_controllerPort >= 0) {
pad_analogX = (_ctrlData.c[_controllerPort].throttle >> 8) & 0xFF;
pad_analogY = (_ctrlData.c[_controllerPort].throttle >> 0) & 0xFF;
}
pad_mouseX = 0;
pad_mouseY = 0;
if (_mousePort >= 0) { // If mouse is present, read movement values
pad_mouseX = (_ctrlData.c[_mousePort].throttle >> 8) & 0xFF;
pad_mouseY = (_ctrlData.c[_mousePort].throttle >> 0) & 0xFF;
}
float mx = _tempMouseX;
float my = _tempMouseY;
// Limit the analog range for pad.
// When moving in diagonal the max/min of 128/-128 was not reached
// yielding weird results for the tangent acceleration function
if (pad_analogX > 60) pad_analogX = 60;
else if (pad_analogX < -60) pad_analogX = -60;
if (pad_analogY > 60) pad_analogY = 60;
else if (pad_analogY < -60) pad_analogY = -60;
// Gamepad
if (abs(pad_analogX) > PAD_DEADZONE)
mx += tan(pad_analogX * (M_PI / 140));
if (abs(pad_analogY) > PAD_DEADZONE)
my -= tan(pad_analogY * (M_PI / 140));
// Mouse
if (abs(pad_mouseX) > MOUSE_DEADZONE)
mx += (pad_mouseX / MOUSE_SENSIBILITY);
if (abs(pad_mouseY) > MOUSE_DEADZONE)
my -= (pad_mouseY / MOUSE_SENSIBILITY);
if (mx < 0)
mx = 0;
if (mx >= _mouseMaxX)
mx = _mouseMaxX - 1;
if (my < 0)
my = 0;
if (my >= _mouseMaxY)
my = _mouseMaxY - 1;
_tempMouseX = mx;
_tempMouseY = my;
}
bool OSystem_N64::pollEvent(Common::Event &event) {
// Check Timers. Not the best place, but checking in interrupts proved to be unsafe
checkTimers();
// Refill audio buffers, doing this inside interrupts could be harmful
refillAudioBuffers();
// Read current controller status
controller_Read_Buttons(&_ctrlData);
static uint16 oldButtons = 0; // old button data... used for button press/release
static uint16 oldMouseButtons = 0;
uint16 newButtons = 0;
if (_controllerPort >= 0)
newButtons = _ctrlData.c[_controllerPort].buttons; // Read from controller
uint16 newMouseButtons = 0;
if (_mousePort >= 0)
newMouseButtons = _ctrlData.c[_mousePort].buttons;
bool buttonPressed = false;
static bool left_digital = false;
static bool right_digital = false;
static bool up_digital = false;
static bool down_digital = false;
if (newButtons != oldButtons) { // Check PAD button press
if (PRESSED_DL(newButtons, oldButtons)) // Pressed LEFT
left_digital = true;
else if (RELEASED_DL(newButtons, oldButtons)) // Released LEFT
left_digital = false;
if (PRESSED_DR(newButtons, oldButtons)) // Pressed RIGHT
right_digital = true;
else if (RELEASED_DR(newButtons, oldButtons)) // Released RIGHT
right_digital = false;
if (PRESSED_DU(newButtons, oldButtons)) // Pressed UP
up_digital = true;
else if (RELEASED_DU(newButtons, oldButtons)) // Released UP
up_digital = false;
if (PRESSED_DD(newButtons, oldButtons)) // Pressed DOWN
down_digital = true;
else if (RELEASED_DD(newButtons, oldButtons)) // Released DOWN
down_digital = false;
// Check if there is a button pressed, apart from DPAD
if ((newButtons & 0xF0FF) != (oldButtons & 0xF0FF))
buttonPressed = true;
if (PRESSED_B(newButtons, oldButtons)) { // Pressed B - Right Mouse Button
event.type = Common::EVENT_RBUTTONDOWN;
} else if (RELEASED_B(newButtons, oldButtons)) { // Released B
event.type = Common::EVENT_RBUTTONUP;
} else if (PRESSED_A(newButtons, oldButtons)) { // Pressed A - Period
event.kbd.keycode = Common::KEYCODE_PERIOD;
event.kbd.ascii = '.';
event.type = Common::EVENT_KEYDOWN;
} else if (RELEASED_A(newButtons, oldButtons)) { // Released A
event.kbd.keycode = Common::KEYCODE_PERIOD;
event.kbd.ascii = '.';
event.type = Common::EVENT_KEYUP;
} else if (PRESSED_START(newButtons, oldButtons)) { // Pressed START
event.kbd.keycode = Common::KEYCODE_F5;
event.kbd.ascii = Common::ASCII_F5;
event.type = Common::EVENT_KEYDOWN;
} else if (RELEASED_START(newButtons, oldButtons)) { // Released START
event.kbd.keycode = Common::KEYCODE_F5;
event.kbd.ascii = Common::ASCII_F5;
event.type = Common::EVENT_KEYUP;
} else if (PRESSED_TL(newButtons, oldButtons)) { // Pressed Trigger Left - ESC
event.kbd.keycode = Common::KEYCODE_ESCAPE;
event.kbd.ascii = 27;
event.type = Common::EVENT_KEYDOWN;
} else if (RELEASED_TL(newButtons, oldButtons)) { // Released Trigger Left
event.kbd.keycode = Common::KEYCODE_ESCAPE;
event.kbd.ascii = 27;
event.type = Common::EVENT_KEYUP;
} else if (PRESSED_TR(newButtons, oldButtons)) { // Pressed Trigger Right - F7
event.kbd.keycode = Common::KEYCODE_F7;
event.kbd.ascii = Common::ASCII_F7;
event.type = Common::EVENT_KEYDOWN;
} else if (RELEASED_TR(newButtons, oldButtons)) { // Released Trigger Right
event.kbd.keycode = Common::KEYCODE_F7;
event.kbd.ascii = Common::ASCII_F7;
event.type = Common::EVENT_KEYUP;
} else if (PRESSED_Z(newButtons, oldButtons)) { // Pressed Z - Left Mouse Button
event.type = Common::EVENT_LBUTTONDOWN;
} else if (RELEASED_Z(newButtons, oldButtons)) { // Released Z
event.type = Common::EVENT_LBUTTONUP;
}
uint8 curKPad = 0; // Current simulated keypad button press
static uint8 lastKPad = 0; // Previously simulated keypad button press
// Check which directions are pressed
if (CU_BUTTON(newButtons)) {
if (CL_BUTTON(newButtons)) {
curKPad = 7;
} else if (CR_BUTTON(newButtons)) {
curKPad = 9;
} else {
curKPad = 8;
}
} else if (CD_BUTTON(newButtons)) {
if (CL_BUTTON(newButtons)) {
curKPad = 1;
} else if (CR_BUTTON(newButtons)) {
curKPad = 3;
} else {
curKPad = 2;
}
} else if (CL_BUTTON(newButtons)) {
curKPad = 4;
} else if (CR_BUTTON(newButtons)) {
curKPad = 6;
}
switch (lastKPad) {
case 7: // UP - LEFT
if (curKPad != 7) {
event.kbd.keycode = Common::KEYCODE_KP7;
event.type = Common::EVENT_KEYUP;
lastKPad = 0;
}
break;
case 9: // UP - RIGHT
if (curKPad != 9) {
event.kbd.keycode = Common::KEYCODE_KP9;
event.type = Common::EVENT_KEYUP;
lastKPad = 0;
}
break;
case 1: // DOWN - LEFT
if (curKPad != 1) {
event.kbd.keycode = Common::KEYCODE_KP1;
event.type = Common::EVENT_KEYUP;
lastKPad = 0;
}
break;
case 3: // DOWN - RIGHT
if (curKPad != 3) {
event.kbd.keycode = Common::KEYCODE_KP3;
event.type = Common::EVENT_KEYUP;
lastKPad = 0;
}
break;
case 4: // LEFT
if (curKPad != 4) {
event.kbd.keycode = Common::KEYCODE_KP4;
event.type = Common::EVENT_KEYUP;
lastKPad = 0;
}
break;
case 6: // RIGHT
if (curKPad != 6) {
event.kbd.keycode = Common::KEYCODE_KP6;
event.type = Common::EVENT_KEYUP;
lastKPad = 0;
}
break;
case 8: // UP
if (curKPad != 8) {
event.kbd.keycode = Common::KEYCODE_KP8;
event.type = Common::EVENT_KEYUP;
lastKPad = 0;
}
break;
case 2: // DOWN
if (curKPad != 2) {
event.kbd.keycode = Common::KEYCODE_KP2;
event.type = Common::EVENT_KEYUP;
lastKPad = 0;
}
break;
case 0: // No previous press
if (curKPad == 7) { // UP - LEFT
event.type = Common::EVENT_KEYDOWN;
event.kbd.keycode = Common::KEYCODE_KP7;
} else if (curKPad == 9) { // UP - RIGHT
event.type = Common::EVENT_KEYDOWN;
event.kbd.keycode = Common::KEYCODE_KP9;
} else if (curKPad == 1) { // DOWN - LEFT
event.type = Common::EVENT_KEYDOWN;
event.kbd.keycode = Common::KEYCODE_KP1;
} else if (curKPad == 3) { // DOWN - RIGHT
event.type = Common::EVENT_KEYDOWN;
event.kbd.keycode = Common::KEYCODE_KP3;
} else if (curKPad == 4) { // LEFT
event.type = Common::EVENT_KEYDOWN;
event.kbd.keycode = Common::KEYCODE_KP4;
} else if (curKPad == 6) { // RIGHT
event.type = Common::EVENT_KEYDOWN;
event.kbd.keycode = Common::KEYCODE_KP6;
} else if (curKPad == 8) { // UP
event.type = Common::EVENT_KEYDOWN;
event.kbd.keycode = Common::KEYCODE_KP8;
} else if (curKPad == 2) { // DOWN
event.type = Common::EVENT_KEYDOWN;
event.kbd.keycode = Common::KEYCODE_KP2;
}
lastKPad = curKPad;
break;
default:
lastKPad = 0;
break; // Do nothing.
}
// A simulated keypad has been "pressed", input the ascii code
if (curKPad != 0) {
event.kbd.ascii = event.kbd.keycode - Common::KEYCODE_KP0 + '0';
}
oldButtons = newButtons; // Save current button status
if (buttonPressed) {
event.mouse.x = _mouseX;
event.mouse.y = _mouseY;
return true;
}
}
if (newMouseButtons != oldMouseButtons) { // Check mouse button press
buttonPressed = true;
if (PRESSED_B(newMouseButtons, oldMouseButtons)) { // Pressed Right Mouse Button
event.type = Common::EVENT_RBUTTONDOWN;
} else if (RELEASED_B(newMouseButtons, oldMouseButtons)) { // Released RMB
event.type = Common::EVENT_RBUTTONUP;
} else if (PRESSED_A(newMouseButtons, oldMouseButtons)) { // Pressed Left Mouse Button
event.type = Common::EVENT_LBUTTONDOWN;
} else if (RELEASED_A(newMouseButtons, oldMouseButtons)) { // Released LMB
event.type = Common::EVENT_LBUTTONUP;
}
oldMouseButtons = newMouseButtons; // Save current button status
if (buttonPressed) {
event.mouse.x = _mouseX;
event.mouse.y = _mouseY;
return true;
}
}
static uint32 _lastPadCheck = 0;
uint32 curTime = getMillis();
if ((curTime - _lastPadCheck) > PAD_CHECK_TIME) {
_lastPadCheck = curTime;
float mx = _tempMouseX;
float my = _tempMouseY;
if (left_digital || right_digital || up_digital || down_digital) {
if (left_digital)
mx -= 2;
else if (right_digital)
mx += 2;
if (up_digital)
my -= 2;
else if (down_digital)
my += 2;
}
if (mx < 0)
mx = 0;
if (mx >= _mouseMaxX)
mx = _mouseMaxX - 1;
if (my < 0)
my = 0;
if (my >= _mouseMaxY)
my = _mouseMaxY - 1;
if ((mx != _mouseX) || (my != _mouseY)) {
event.type = Common::EVENT_MOUSEMOVE;
event.mouse.x = _mouseX = _tempMouseX = mx;
event.mouse.y = _mouseY = _tempMouseY = my;
_dirtyOffscreen = true;
return true;
}
}
return false;
}

View File

@@ -0,0 +1,116 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "osys_n64.h"
#include "backends/timer/default/default-timer.h"
void checkTimers(void) {
OSystem_N64 *osys = dynamic_cast<OSystem_N64 *>(g_system);
uint32 curTime = osys->getMillis();
// Timer checking & firing
if (osys->_timerCallback && (curTime >= osys->_timerCallbackNext)) {
osys->_timerCallback(osys->_timerCallbackTimer);
osys->_timerCallbackNext = curTime + osys->_timerCallbackTimer;
}
}
void disableAudioPlayback(void) {
if (!_audioEnabled) return;
_audioEnabled = false;
while (AI_busy()); // Wait for audio to stop
}
void enableAudioPlayback(void) {
static bool _firstRun = true;
OSystem_N64 *osys = dynamic_cast<OSystem_N64 *>(g_system);
Audio::MixerImpl *localmixer = (Audio::MixerImpl *)osys->getMixer();
uint32 sampleBufferSize = 3072;
initAudioInterface(osys->_viClockRate, DEFAULT_SOUND_SAMPLE_RATE, 16, sampleBufferSize);
osys->_audioBufferSize = getAIBufferSize();
if (_firstRun) {
localmixer->setReady(true);
_firstRun = false;
}
disable_interrupts();
_audioEnabled = true;
sndCallback();
sndCallback();
registerAIhandler(sndCallback); // Lib checks if i try to register it multiple times
enable_interrupts();
}
static volatile Uint32 _requiredSoundSlots = 0;
void vblCallback(void) {
// Switch display buffer
switchDisplayBuffer();
// If audio buffer got depleted, ask for more slots to refill.
if (_audioEnabled && !AI_busy() && !_requiredSoundSlots) {
sndCallback();
sndCallback();
}
dynamic_cast<OSystem_N64 *>(g_system)->readControllerAnalogInput();
}
void sndCallback() {
// Signal that an audio buffer finished playing and that we need more samples
if (_requiredSoundSlots < 2)
_requiredSoundSlots++;
}
void refillAudioBuffers(void) {
if (!_audioEnabled) return;
OSystem_N64 *osys = dynamic_cast<OSystem_N64 *>(g_system);
byte *sndBuf;
Audio::MixerImpl *localmixer = (Audio::MixerImpl *)osys->getMixer();
while (_requiredSoundSlots) {
sndBuf = (byte *)getAIBuffer();
localmixer->mixCallback((byte *)sndBuf, osys->_audioBufferSize);
putAIBuffer();
_requiredSoundSlots--;
}
}
int timer_handler(int t) {
DefaultTimerManager *tm = (DefaultTimerManager *)g_system->getTimerManager();
tm->handler();
return t;
}

View File

@@ -0,0 +1,12 @@
#!/bin/bash
TARGET=$1
BASESIZE=2097152
CARTSIZE=`ls -l $1 | cut -d" " -f5`
REMAINDER=`echo $CARTSIZE % $BASESIZE | bc`
REMAINDER=`echo $BASESIZE - $REMAINDER | bc`
CARTSIZE=`echo $CARTSIZE + $REMAINDER | bc`
ucon64 -q --n64 --v64 --chk --padn=$CARTSIZE $1

View File

@@ -0,0 +1,71 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <n64utils.h>
#include "pakfs_save_manager.h"
bool pakfs_deleteSaveGame(const char *filename) {
int res = removeFileOnPak(filename);
flushCurrentPakData();
return (res == 0);
}
uint32 InPAKSave::read(void *buf, uint32 cnt) {
return pakfs_read(buf, 1, cnt, fd);
}
bool InPAKSave::seek(int64 offs, int whence) {
pakfs_seek(fd, offs, whence);
return true;
}
bool InPAKSave::skip(uint32 offset) {
pakfs_seek(fd, offset, SEEK_CUR);
return true;
}
uint32 OutPAKSave::write(const void *buf, uint32 cnt) {
return pakfs_write(buf, 1, cnt, fd);
}
Common::StringArray PAKSaveManager::listSavefiles(const Common::String &pattern) {
PAKDIR *dirp = pakfs_opendir();
pakfs_dirent *dp;
Common::StringArray list;
Common::String *fname;
while ((dp = pakfs_readdir(dirp)) != NULL) {
fname = new Common::String(dp->entryname);
if (fname->matchString(pattern, false, NULL))
list.push_back(dp->entryname);
delete fname;
free(dp);
}
pakfs_closedir(dirp);
return list;
}

View File

@@ -0,0 +1,154 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef __PAKFS_SAVE_MANAGER__
#define __PAKFS_SAVE_MANAGER__
#include <common/savefile.h>
#include <common/compression/deflate.h>
#include <pakfs.h> // N64 PakFS library
bool pakfs_deleteSaveGame(const char *filename);
class InPAKSave : public Common::InSaveFile {
private:
PAKFILE *fd;
uint32 read(void *buf, uint32 cnt) override;
bool skip(uint32 offset) override;
bool seek(int64 offs, int whence) override;
public:
InPAKSave() : fd(NULL) { }
~InPAKSave() {
if (fd != NULL)
pakfs_close(fd);
}
bool eos() const override {
return pakfs_eof(fd);
}
void clearErr() override {
pakfs_clearerr(fd);
}
int64 pos() const override {
return pakfs_tell(fd);
}
int64 size() const override {
return fd->size;
}
bool readSaveGame(const char *filename) {
fd = pakfs_open(filename, "r");
return (fd != NULL);
}
};
class OutPAKSave : public Common::WriteStream {
private:
PAKFILE *fd;
public:
uint32 write(const void *buf, uint32 cnt);
virtual int64 pos() const {
return pakfs_tell(fd);
}
OutPAKSave(const char *_filename) : fd(NULL) {
fd = pakfs_open(_filename, "w");
}
~OutPAKSave() {
if (fd != NULL) {
finalize();
pakfs_close(fd);
flushCurrentPakData();
}
}
bool err() const {
if (fd)
return (pakfs_error(fd) == 1);
else
return true;
}
void clearErr() {
pakfs_clearerr(fd);
}
void finalize() {
pakfs_flush(fd);
}
};
class PAKSaveManager : public Common::SaveFileManager {
public:
void updateSavefilesList(Common::StringArray &lockedFiles) override {
// this method is used to lock saves while cloud syncing
// as there is no network on N64, this method wouldn't be used
// thus it's not implemtented
}
Common::InSaveFile *openRawFile(const Common::String &filename) override {
InPAKSave *s = new InPAKSave();
if (s->readSaveGame(filename.c_str())) {
return s;
} else {
delete s;
return NULL;
}
}
Common::OutSaveFile *openForSaving(const Common::String &filename, bool compress = true) override {
OutPAKSave *s = new OutPAKSave(filename.c_str());
if (!s->err()) {
return new Common::OutSaveFile(compress ? Common::wrapCompressedWriteStream(s) : s);
} else {
delete s;
return NULL;
}
}
Common::InSaveFile *openForLoading(const Common::String &filename) override {
InPAKSave *s = new InPAKSave();
if (s->readSaveGame(filename.c_str())) {
return Common::wrapCompressedReadStream(s);
} else {
delete s;
return NULL;
}
}
bool removeSavefile(const Common::String &filename) override {
return ::pakfs_deleteSaveGame(filename.c_str());
}
Common::StringArray listSavefiles(const Common::String &pattern) override;
bool exists(const Common::String &filename) override {
return InPAKSave().readSaveGame(filename.c_str());
}
};
#endif

View File

@@ -0,0 +1,44 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef __N64_PORTDEFS__
#define __N64_PORTDEFS__
#include <n64utils.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stddef.h>
// No assert.h
#include <ctype.h>
#include <inttypes.h>
#include <limits.h>
#include <math.h>
#include <new>
#include <limits>
#undef assert
#define assert(x) ((x) ? 0 : (print_error("ASSERT TRIGGERED:\n\n("#x")\n%s\nline: %d", __FILE__, __LINE__)))
#endif // __N64_PORTDEFS__