Initial commit
This commit is contained in:
91
backends/platform/n64/Makefile
Normal file
91
backends/platform/n64/Makefile
Normal 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
|
||||
115
backends/platform/n64/README.N64
Normal file
115
backends/platform/n64/README.N64
Normal 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.
|
||||
70
backends/platform/n64/framfs_save_manager.cpp
Normal file
70
backends/platform/n64/framfs_save_manager.cpp
Normal 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;
|
||||
}
|
||||
152
backends/platform/n64/framfs_save_manager.h
Normal file
152
backends/platform/n64/framfs_save_manager.h
Normal 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
|
||||
14
backends/platform/n64/module.mk
Normal file
14
backends/platform/n64/module.mk
Normal 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)))
|
||||
33
backends/platform/n64/n64.mk
Normal file
33
backends/platform/n64/n64.mk
Normal 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
|
||||
32
backends/platform/n64/nintendo64.cpp
Normal file
32
backends/platform/n64/nintendo64.cpp
Normal 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;
|
||||
}
|
||||
209
backends/platform/n64/osys_n64.h
Normal file
209
backends/platform/n64/osys_n64.h
Normal 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__ */
|
||||
909
backends/platform/n64/osys_n64_base.cpp
Normal file
909
backends/platform/n64/osys_n64_base.cpp
Normal 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);
|
||||
}
|
||||
440
backends/platform/n64/osys_n64_events.cpp
Normal file
440
backends/platform/n64/osys_n64_events.cpp
Normal 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;
|
||||
}
|
||||
116
backends/platform/n64/osys_n64_utilities.cpp
Normal file
116
backends/platform/n64/osys_n64_utilities.cpp
Normal 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;
|
||||
}
|
||||
12
backends/platform/n64/pad_rom.sh
Normal file
12
backends/platform/n64/pad_rom.sh
Normal 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
|
||||
71
backends/platform/n64/pakfs_save_manager.cpp
Normal file
71
backends/platform/n64/pakfs_save_manager.cpp
Normal 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;
|
||||
}
|
||||
154
backends/platform/n64/pakfs_save_manager.h
Normal file
154
backends/platform/n64/pakfs_save_manager.h
Normal 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
|
||||
44
backends/platform/n64/portdefs.h
Normal file
44
backends/platform/n64/portdefs.h
Normal 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__
|
||||
Reference in New Issue
Block a user