commit
c65006789f
22
.vscode/c_cpp_properties.json
vendored
Normal file
22
.vscode/c_cpp_properties.json
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${default}"
|
||||
],
|
||||
"defines": [
|
||||
"__USE_POSIX",
|
||||
"__USE_MISC"
|
||||
],
|
||||
"cppStandard": "gnu++17",
|
||||
"cStandard": "c99",
|
||||
"intelliSenseMode": "linux-gcc-x86",
|
||||
"forcedInclude": [
|
||||
"${workspaceFolder}/../vscode-preinclude.h"
|
||||
],
|
||||
"compilerArgs": []
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@ -9,5 +9,8 @@
|
||||
"ioctl.h": "c",
|
||||
"segaeax.h": "c",
|
||||
"passthrough.h": "c"
|
||||
}
|
||||
},
|
||||
"C_Cpp.errorSquiggles": "disabled",
|
||||
"C_Cpp.default.intelliSenseMode": "linux-gcc-x86",
|
||||
"C_Cpp.default.compilerPath": "/usr/bin/gcc"
|
||||
}
|
||||
|
12
Makefile
12
Makefile
@ -1,5 +1,5 @@
|
||||
CC=gcc -m32
|
||||
CFLAGS = -g -O0 -fPIC -m32 -D_GNU_SOURCE -Wall -Werror -Wno-unused-variable -Wno-unused-function
|
||||
CFLAGS = -g -O0 -fPIC -m32 -Wall -Werror -Wno-unused-variable -Wno-unused-function
|
||||
LD = g++ -m32
|
||||
LDFLAGS = -Wl,-z,defs -rdynamic -static-libstdc++ -static-libgcc -lc -ldl -lGL -lglut -lX11 -lm -lpthread -shared -nostdlib
|
||||
|
||||
@ -7,7 +7,11 @@ BUILD = build
|
||||
|
||||
OBJS := $(patsubst %.c,%.o,$(wildcard src/lindbergh/*.c))
|
||||
|
||||
all: lindbergh.so libsegaapi.so libkswapapi.so
|
||||
all: lindbergh lindbergh.so libsegaapi.so libkswapapi.so
|
||||
|
||||
lindbergh: src/lindbergh/lindbergh.c
|
||||
mkdir -p $(BUILD)
|
||||
$(CC) src/lindbergh/lindbergh.c -o $(BUILD)/lindbergh
|
||||
|
||||
lindbergh.so: $(OBJS)
|
||||
mkdir -p $(BUILD)
|
||||
@ -23,6 +27,10 @@ libsegaapi.so: src/libsegaapi/segaapi.o
|
||||
|
||||
libkswapapi.so: src/libkswapapi/libkswapapi.o
|
||||
$(LIBSEGA_LD) $(LIBSEGA_LDFLAGS) src/libkswapapi/libkswapapi.o -L/usr/lib/i386-linux-gnu -fPIC -shared -o $(BUILD)/libkswapapi.so
|
||||
rm -f src/libkswapapi/*.o
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD)
|
||||
rm -f src/lindbergh/*.o
|
||||
rm -f src/libsegaapi/*.o
|
||||
rm -f src/libkswapapi/*.o
|
||||
|
21
README.md
21
README.md
@ -1,16 +1,12 @@
|
||||
# SEGA Lindbergh Emulator
|
||||
|
||||
This project aims to hook and emulate the various different parts of the SEGA Lindbergh allowing the games to run on modern versions of Linux.
|
||||
This alpha stage project emulates the SEGA Lindbergh, allowing games to run on modern Linux computers with any NVIDIA graphics card.
|
||||
|
||||
You can view the supported titles [here.](docs/supported.md)
|
||||
|
||||
You will need an nvidia graphics card and I have tested with the latest version of Ubuntu.
|
||||
|
||||
Please be aware that the project is in very early stages, and there will be lots of issues with all games.
|
||||
|
||||
## Dependencies
|
||||
|
||||
First make sure you have up to date nVidia drivers for your computer, and then install the following:
|
||||
First make sure you have up to date NVIDIA drivers and then install the following:
|
||||
|
||||
```
|
||||
sudo dpkg --add-architecture i386
|
||||
@ -32,7 +28,7 @@ sudo addgroup $USER dialout
|
||||
sudo addgroup $USER input
|
||||
```
|
||||
|
||||
To build, run the makefile, and then copy the contents of the build directory into your game directory and run.
|
||||
To build, run the makefile, and then copy the contents of the build directory into the game directory and run.
|
||||
|
||||
```
|
||||
make
|
||||
@ -41,11 +37,13 @@ cd ~/the-house-of-the-dead-4/disk0/elf
|
||||
LD_PRELOAD=lindbergh.so LD_LIBRARY_PATH=$LD_LIBRARY_PATH:. ./hod4M.elf
|
||||
```
|
||||
|
||||
Some games will require extra libraries like `libposixtime.so`. These can be found in any dumps of the Lindbergh CF image.
|
||||
Some games will require extra libraries like `libposixtime.so`, which can be found in dumps of the Lindbergh CF image.
|
||||
|
||||
A default configuration file is provided in `docs/lindbergh.conf`. It should be placed in the same folder as the game is run from. If no config file is present a default setting will be used.
|
||||
A default configuration file is provided in `docs/lindbergh.conf`. It should be placed in the same folder the game is run from. If no config file is present a default setting will be used.
|
||||
|
||||
I recomend that you do not run this as root, and instead use the usergroups for input/dialout to give the emulator access to what it needs. The Lindbergh games expect full control of the Linux OS and with root privilages it is possible that they could cause damage to your computer.
|
||||
Do not run this as root, instead use the usergroups for input/dialout to give the emulator access to what it needs. Lindbergh games expect full control of the Linux OS and with root privilages it is possible that they could cause damage to your computer.
|
||||
|
||||
A `lindbergh` executable is provided in the build directory to easily run the games. Place it in the same directory as the game elf, and run `./lindbergh` to automatically start the game with the correct environment variables set, or run `./lindbergh -t` for test mode.
|
||||
|
||||
## Controls
|
||||
|
||||
@ -62,5 +60,4 @@ Currently the controls are set up for The House of the Dead 4.
|
||||
|
||||
## Thanks
|
||||
|
||||
This project has been built by referencing things made by Teknoparrot, Doozer and JayFoxRox and from contributions by Rolel and
|
||||
dkeruza-neo and extensive testing by Francesco so thanks to all of them!
|
||||
This project has been built by referencing earlier projects by Teknoparrot and JayFoxRox and from contributions by Doozer, Rolel and dkeruza-neo with extensive testing by Francesco - thanks to all of them!
|
||||
|
94
docs/games.txt
Normal file
94
docs/games.txt
Normal file
@ -0,0 +1,94 @@
|
||||
|
||||
2SPICY
|
||||
After Burner
|
||||
After Burner Climax
|
||||
After Burner Climax [Rev.A]
|
||||
After Burner Climax [Rev.B]
|
||||
After Burner Climax SDX
|
||||
After Burner Climax SDX [Rev.A]
|
||||
AMI-GYO
|
||||
Ami No.3
|
||||
Answer x Answer
|
||||
Answer x Answer 2
|
||||
Answer x Answer Live!
|
||||
Blackjack Nailed Ace
|
||||
Club Majesty - Extend - Cloud Nine
|
||||
Club Majesty - Extend - Wheel Maniacs
|
||||
Club Majesty Formal: Attractive Deck Poker
|
||||
Club Majesty Formal: Cosmic Challenge
|
||||
Club Majesty Formal: Cosmic Challenge [Rev.C]
|
||||
Club Majesty: Gatling Poker
|
||||
Club Majesty: Where's Wally!
|
||||
Derby Owners Club 2008 Feel the Rush
|
||||
Derby Owners Club 2009 Ride for the live
|
||||
Ghost Squad Evolution
|
||||
Harley Davidson King of the Road
|
||||
HOTD4
|
||||
HOTD4SP
|
||||
HOTD EX
|
||||
Hummer
|
||||
Hummer Extreme
|
||||
Hummer Extreme Edition MDX
|
||||
Info Station 2 [Rev.C]
|
||||
Initial D Arcade Stage 4
|
||||
Initial D Arcade Stage 4 [Rev.A]
|
||||
Initial D Arcade Stage 4 [Rev.B]
|
||||
Initial D Arcade Stage 4 [Rev.C]
|
||||
Initial D Arcade Stage 4 [Rev.D]
|
||||
Initial D Arcade Stage 4 [Rev.G]
|
||||
Initial D Arcade Stage 5
|
||||
Initial D Arcade Stage 5 Exp 2.0
|
||||
Initial D Arcade Stage 5 [Rev.A]
|
||||
Let's Go Jungle
|
||||
Let's Go Jungle Special
|
||||
MJ4 Evolution
|
||||
Outrun 2 SP SDX
|
||||
Outrun 2 SP SDX [Rev.A]
|
||||
Primeval Hunt
|
||||
Rambo
|
||||
Rambo (China)
|
||||
Router Update (for VTF)
|
||||
R-Tuned
|
||||
Sangokushi Taisen 2
|
||||
Sangokushi Taisen 3
|
||||
Sega Network Casino Club STD
|
||||
Sega Network Casino Club Ver.2
|
||||
Sega Network Casino Club Ver.2 [Rev.B]
|
||||
Sega Network Casino Club Ver.3
|
||||
Sega Race TV
|
||||
Sengokushi Taisen3 War Begins
|
||||
Star Horse 2 Fifth Expansion [Rev.B]
|
||||
Star Horse 2 Fifth Expansion [Rev.D]
|
||||
Star Horse 2 Fifth Expansion [Rev.E]
|
||||
Star Horse 2 Final Destination
|
||||
Starhorse 2 Fourth Ambition
|
||||
Star Horse 2 Fourth Ambition
|
||||
Star Horse 2 New Generation
|
||||
Star Horse 2 Second Fusion
|
||||
Star Horse 2 Third Evolution
|
||||
Star Horse 2 Third Evolution (Hong Kong)
|
||||
Taisen Mahjong 4 (1.0)
|
||||
Taisen Mahjong 4 (2.0)
|
||||
Taisen Mahjong 4 (3.0)
|
||||
Taisen Mahjong 4 (4.0)
|
||||
Taisen Mahjong 4 (5.0)
|
||||
Taisen Mahjong 4 (6.0)
|
||||
Taisen Mahjong 4 (7.0)
|
||||
Taisen Mahjong 4 (8.0)
|
||||
toAmi-Gyo
|
||||
to AMI GYO
|
||||
VBIOS Update (2.0)
|
||||
VBIOS Update (3.0)
|
||||
VBIOS Update (for VTF)
|
||||
Virtua Fighter 5
|
||||
Virtua Fighter 5 Final Showdown
|
||||
Virtua Fighter 5 Final Showdown [Rev.A]
|
||||
Virtua Fighter 5 R
|
||||
Virtua Fighter 5 [Rev.A]
|
||||
Virtua Fighter 5 [Rev.B]
|
||||
Virtua Fighter 5 [Rev.E]
|
||||
Virtua Fighter 5 R [Rev.D]
|
||||
Virtua Tennis 3
|
||||
World Club Championship Football 2008-2009
|
||||
World Club Championship Football 2009-2010 [ASIA]
|
||||
World Club Championship Football 2009-2010 [JP]
|
@ -1,7 +1,6 @@
|
||||
# SEGA Lindbergh Emulator Configuration File
|
||||
# Written by Bobby Dilley
|
||||
|
||||
|
||||
# Set the colour of the lindbergh to change the Segaboot logo
|
||||
# Possibly colours are: YELLOW and RED
|
||||
LINDBERGH_COLOUR YELLOW
|
||||
@ -22,6 +21,11 @@ WIDTH 640
|
||||
# Set the requested dip switch height here
|
||||
HEIGHT 480
|
||||
|
||||
# Set if the emulator should emulate JVS and use the keyboard/mouse for controls.
|
||||
# If this is set to 0, then the emulator will route the traffic to the serial device
|
||||
# defined in RIDEBOARD_PATH if it has been defined.
|
||||
EMULATE_JVS 1
|
||||
|
||||
# Set if the emulator should emulate the rideboard used in the special games here
|
||||
# If this is set to 0, then the emulator will route the traffic to the serial device
|
||||
# defined in RIDEBOARD_PATH if it has been defined.
|
||||
@ -37,22 +41,32 @@ EMULATE_DRIVEBOARD 0
|
||||
# defined in MOTIONBOARD_PATH if it has been defined.
|
||||
EMULATE_MOTIONBOARD 0
|
||||
|
||||
# Set if the emulator should emulate JVS and use the keyboard/mouse for controls.
|
||||
# If this is set to 0, then the emulator will route the traffic to the serial device
|
||||
# defined in RIDEBOARD_PATH if it has been defined.
|
||||
EMULATE_JVS 1
|
||||
|
||||
# Define the path to pass the JVS packets to
|
||||
# JVS_PATH /dev/ttyUSB0
|
||||
JVS_PATH /dev/ttyUSB0
|
||||
|
||||
# Define the path to pass the rideboard packets to
|
||||
# RIDEBOARD_PATH /dev/ttyUSB0
|
||||
RIDEBOARD_PATH /dev/ttyUSB0
|
||||
|
||||
# Define the path to pass the driveboard packets to
|
||||
# DRIVEBOARD_PATH /dev/ttyUSB0
|
||||
DRIVEBOARD_PATH /dev/ttyUSB0
|
||||
|
||||
# Define the path to pass the motionboard packets to
|
||||
# MOTIONBOARD_PATH /dev/ttyUSB0
|
||||
MOTIONBOARD_PATH /dev/ttyUSB0
|
||||
|
||||
# Define the path to the sram.bin file
|
||||
SRAM_PATH sram.bin
|
||||
|
||||
# Define the path to the eeprom.bin file
|
||||
EEPROM_PATH eeprom.bin
|
||||
|
||||
# Set if the emulator should go full screen
|
||||
FULLSCREEN 0
|
||||
|
||||
# Set the Region ( JP/US/EX )
|
||||
REGION EX
|
||||
|
||||
# Set if you want the game to be Free Play
|
||||
FREEPLAY 1
|
||||
|
||||
# Set if you want to see degug messages in the console
|
||||
DEBUG_MSGS 0
|
||||
|
@ -10,7 +10,8 @@ Working is defined by getting into attract mode and running the game, but not ne
|
||||
- After Burner Climax
|
||||
- Ghost Squad Evolution
|
||||
- Harley Davidson
|
||||
- Let's Go Jungle Special (boots to ride error)
|
||||
- Let's Go Jungle
|
||||
- Let's Go Jungle Special
|
||||
- Outrun 2 SP SDX
|
||||
- R-Tuned
|
||||
- Race TV
|
||||
@ -19,20 +20,12 @@ Working is defined by getting into attract mode and running the game, but not ne
|
||||
- The House Of The Dead 4 Special
|
||||
- The House Of The Dead Ex
|
||||
- Virtua Fighter 5
|
||||
|
||||
## Games that do not work or haven't been tested
|
||||
|
||||
- Hummer
|
||||
- Initial D 4
|
||||
- Initial D 5
|
||||
- Let's Go Jungle
|
||||
- Primevil
|
||||
- Virtua Tennis 3
|
||||
|
||||
## How to run specific games
|
||||
## Games that currently do not work
|
||||
|
||||
### Let's Go Jungle
|
||||
- Hummer
|
||||
- Initial D 5
|
||||
- Primevil
|
||||
|
||||
No, this game will not start.
|
||||
|
||||
```LD_LIBRARY_PATH=$LD_LIBRARY_PATH:. LD_PRELOAD=lindbergh.so TEA_DIR=`pwd` ./lgj_final```
|
||||
|
Binary file not shown.
@ -4,6 +4,7 @@
|
||||
#include <fcntl.h> /* File control definitions */
|
||||
#include <errno.h> /* Error number definitions */
|
||||
#include <stdlib.h> /* Standard library functions like malloc, free, exit, and atoi */
|
||||
#include <sys/select.h>
|
||||
|
||||
#include "baseboard.h"
|
||||
|
||||
@ -59,6 +60,7 @@ uint8_t sharedMemory[1024 * 32] = {0};
|
||||
|
||||
int selectReply = -1;
|
||||
int jvsFileDescriptor = -1;
|
||||
int jvsPacketSize = -1;
|
||||
|
||||
int initBaseboard()
|
||||
{
|
||||
@ -191,7 +193,7 @@ int baseboardIoctl(int fd, unsigned int request, void *data)
|
||||
|
||||
if (getConfig()->emulateJVS)
|
||||
{
|
||||
processPacket();
|
||||
processPacket(&jvsPacketSize);
|
||||
}
|
||||
else if (jvsFileDescriptor >= 0)
|
||||
{
|
||||
@ -250,9 +252,9 @@ int baseboardIoctl(int fd, unsigned int request, void *data)
|
||||
{
|
||||
if (getConfig()->emulateJVS)
|
||||
{
|
||||
memcpy(&sharedMemory[jvsCommand.destAddress], outputBuffer, outputPacket.length + 3);
|
||||
memcpy(&sharedMemory[jvsCommand.destAddress], outputBuffer, jvsPacketSize);
|
||||
_data[2] = jvsCommand.destAddress;
|
||||
_data[3] = outputPacket.length + 3;
|
||||
_data[3] = jvsPacketSize;
|
||||
_data[1] = 1; // Set the status to success
|
||||
}
|
||||
else if (jvsFileDescriptor >= 0)
|
||||
|
@ -7,6 +7,8 @@
|
||||
|
||||
EmulatorConfig config = {0};
|
||||
|
||||
extern uint32_t elf_crc;
|
||||
|
||||
FILE *configFile = NULL;
|
||||
|
||||
#define CONFIG_PATH "lindbergh.conf"
|
||||
@ -28,69 +30,187 @@ static char *getNextToken(char *buffer, char *seperator, char **saveptr)
|
||||
return token;
|
||||
}
|
||||
|
||||
static int detectGame()
|
||||
static int detectGame(uint32_t elf_crc)
|
||||
{
|
||||
|
||||
// For a better way of doing this, we should look for strings inside the game
|
||||
// This will allow patching the game and for it to still be detected.
|
||||
// strings %s | grep "RIDE TURN TEST" && strings %s | grep "PLEASE SHOOT GRID"
|
||||
// shows you its hotd4S for example
|
||||
|
||||
if (strstr(program_invocation_name, "segaboot"))
|
||||
if (elf_crc == 0x93ea7e11)
|
||||
{
|
||||
config.game = SEGABOOT;
|
||||
config.game = SEGABOOT_2_4;
|
||||
config.gameStatus = WORKING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strstr(program_invocation_name, "hod4"))
|
||||
if (elf_crc == 0x3cc635ee)
|
||||
{
|
||||
config.game = SEGABOOT_2_4_SYM;
|
||||
config.gameStatus = WORKING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (elf_crc == 0xbc0c9ffa)
|
||||
{
|
||||
config.game = THE_HOUSE_OF_THE_DEAD_4;
|
||||
config.gameStatus = WORKING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strstr(program_invocation_name, "Jennifer"))
|
||||
if (elf_crc == 0x5df569f5)
|
||||
{
|
||||
config.game = OUTRUN;
|
||||
config.game = THE_HOUSE_OF_THE_DEAD_4_STRIPPED;
|
||||
config.gameStatus = WORKING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (elf_crc == 0x7235bda8)
|
||||
{
|
||||
config.game = THE_HOUSE_OF_THE_DEAD_4_TEST;
|
||||
config.gameStatus = WORKING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (elf_crc == 0x12266f81)
|
||||
{
|
||||
config.game = THE_HOUSE_OF_THE_DEAD_4_SPECIAL;
|
||||
config.gameStatus = WORKING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (elf_crc == 0x83ba3b45)
|
||||
{
|
||||
config.game = THE_HOUSE_OF_THE_DEAD_4_SPECIAL_TEST;
|
||||
config.gameStatus = WORKING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (elf_crc == 0x85c0c22a)
|
||||
{
|
||||
config.game = THE_HOUSE_OF_THE_DEAD_EX;
|
||||
config.gameStatus = WORKING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (elf_crc == 0xb9a166bb)
|
||||
{
|
||||
config.game = THE_HOUSE_OF_THE_DEAD_EX_TEST;
|
||||
config.gameStatus = WORKING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (elf_crc == 0x6d055308)
|
||||
{
|
||||
config.game = OUTRUN_2_SP_SDX_REVA;
|
||||
config.emulateDriveboard = 1;
|
||||
config.emulateMotionboard = 1;
|
||||
config.gameStatus = WORKING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strstr(program_invocation_name, "JenTest"))
|
||||
if (elf_crc == 0xffdccaaa)
|
||||
{
|
||||
config.game = OUTRUN_TEST;
|
||||
config.game = OUTRUN_2_SP_SDX_REVA_TEST;
|
||||
config.emulateDriveboard = 1;
|
||||
config.emulateMotionboard = 1;
|
||||
config.gameStatus = WORKING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strstr(program_invocation_name, "lgj"))
|
||||
if (elf_crc == 0xd4726d61)
|
||||
{
|
||||
config.game = LETS_GO_JUNGLE;
|
||||
config.gameStatus = WORKING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strstr(program_invocation_name, "abc1080"))
|
||||
if (elf_crc == 0xbbabc0e0)
|
||||
{
|
||||
config.game = ABC;
|
||||
config.game = LETS_GO_JUNGLE_SPECIAL;
|
||||
config.gameStatus = NOT_WORKING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strstr(program_invocation_name, "drive"))
|
||||
if (elf_crc == 0xcc02de7d)
|
||||
{
|
||||
config.game = SRTV;
|
||||
config.game = AFTER_BURNER_CLIMAX;
|
||||
config.gameStatus = WORKING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strstr(program_invocation_name, "dsr"))
|
||||
if (elf_crc == 0x152530dd)
|
||||
{
|
||||
config.game = RTUNED;
|
||||
config.game = AFTER_BURNER_CLIMAX_REVA;
|
||||
config.gameStatus = WORKING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strstr(program_invocation_name, "vf5"))
|
||||
if (elf_crc == 0x4e9ccf33)
|
||||
{
|
||||
config.game = VF5;
|
||||
config.game = INITIALD_4;
|
||||
config.gameStatus = NOT_WORKING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (elf_crc == 0x7f3f9f0c)
|
||||
{
|
||||
config.game = INITIALD_4_REVE;
|
||||
config.gameStatus = NOT_WORKING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (elf_crc == 0xfb096f81)
|
||||
{
|
||||
config.game = SEGA_RACE_TV;
|
||||
config.emulateDriveboard = 1;
|
||||
config.gameStatus = NOT_WORKING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (elf_crc == 0x77ebac34)
|
||||
{
|
||||
config.game = RAMBO;
|
||||
config.gameStatus = NOT_WORKING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (elf_crc == 0xb05d9bbe)
|
||||
{
|
||||
config.game = R_TUNED;
|
||||
config.emulateDriveboard = 1;
|
||||
config.gameStatus = WORKING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (elf_crc == 0x4c768eb4)
|
||||
{
|
||||
config.game = TOO_SPICY;
|
||||
config.gameStatus = WORKING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (elf_crc == 0xc4b7e89)
|
||||
{
|
||||
config.game = VIRTUA_TENNIS_3;
|
||||
config.gameStatus = WORKING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (elf_crc == 0xffe3b0fd)
|
||||
{
|
||||
config.game = VIRTUA_TENNIS_3_TEST;
|
||||
config.gameStatus = NOT_WORKING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (elf_crc == 0x1bf1b627)
|
||||
{
|
||||
config.game = VIRTUA_FIGHTER_5_REVC;
|
||||
config.gameStatus = WORKING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (elf_crc == 0x3CC635EE)
|
||||
{
|
||||
config.game = SEGABOOT_2_4;
|
||||
config.gameStatus = WORKING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -100,32 +220,105 @@ static int detectGame()
|
||||
|
||||
char *getGameName()
|
||||
{
|
||||
char *unknownGameTitle = "Unknown Game";
|
||||
switch (config.game)
|
||||
{
|
||||
case AFTER_BURNER_CLIMAX:
|
||||
return "After Burner Climax";
|
||||
case AFTER_BURNER_CLIMAX_REVA:
|
||||
return "After Burner Climax Rev A";
|
||||
case AFTER_BURNER_CLIMAX_REVB:
|
||||
return "After Burner Climax Rev B";
|
||||
case AFTER_BURNER_CLIMAX_SDX:
|
||||
return "After Burner Climax SDX";
|
||||
case AFTER_BURNER_CLIMAX_SDX_REVA:
|
||||
return "After Burner Climax SDX Rev A";
|
||||
case GHOST_SQUAD_EVOLUTION:
|
||||
return "Ghost Squad Evolution";
|
||||
case HARLEY_DAVIDSON:
|
||||
return "Harley Davidson: King of the Road";
|
||||
case HUMMER:
|
||||
return "Hummer";
|
||||
case HUMMER_EXTREME:
|
||||
return "Hummer Extreme";
|
||||
case HUMMER_EXTREME_MDX:
|
||||
return "Hummer Extreme MDX";
|
||||
case INITIALD_4:
|
||||
return "Initial D Arcade Stage 4";
|
||||
case INITIALD_5:
|
||||
return "Initial D Arcade Stage 5";
|
||||
case LETS_GO_JUNGLE:
|
||||
return "Let's Go Jungle! Lost on the Island of Spice!";
|
||||
case LETS_GO_JUNGLE_SPECIAL:
|
||||
return "Let's Go Jungle! Special!";
|
||||
case OUTRUN_2_SP_SDX:
|
||||
return "Outrun 2 SP SDX";
|
||||
case OUTRUN_2_SP_SDX_REVA:
|
||||
return "Outrun 2 SP SDX Rev A";
|
||||
case OUTRUN_2_SP_SDX_REVA_TEST:
|
||||
return "Outrun 2 SP SDX Rev A Test Mode";
|
||||
case OUTRUN_2_SP_SDX_TEST:
|
||||
return "Outrun 2 SP SDX Test Mode";
|
||||
case PRIMEVAL_HUNT:
|
||||
return "Primeval Hunt";
|
||||
case RAMBO:
|
||||
return "Rambo";
|
||||
case RAMBO_CHINA:
|
||||
return "Rambo China Release";
|
||||
case R_TUNED:
|
||||
return "R-Tuned Ultimate Street Racing";
|
||||
case SEGABOOT:
|
||||
return "SEGABOOT";
|
||||
return "Segaboot";
|
||||
case SEGABOOT_2_4:
|
||||
return "SEGABOOT 2.4";
|
||||
case SEGABOOT_2_4_SYM:
|
||||
return "SEGABOOT 2.4 with Symbols";
|
||||
case SEGABOOT_2_6:
|
||||
return "SEGABOOT 2.6";
|
||||
case OUTRUN:
|
||||
return "Outrun 2 SP";
|
||||
case SEGA_RACE_TV:
|
||||
return "SEGA Race TV";
|
||||
case THE_HOUSE_OF_THE_DEAD_4:
|
||||
return "The House of the Dead 4";
|
||||
case LETS_GO_JUNGLE:
|
||||
return "Let's Go Jungle! Lost on the Island of Spice";
|
||||
case ABC:
|
||||
return "After Burner Climax";
|
||||
case SRTV:
|
||||
return "SEGA Race TV";
|
||||
case RTUNED:
|
||||
return "R-Tuned Ultimate Street Racing";
|
||||
case VF5:
|
||||
case THE_HOUSE_OF_THE_DEAD_4_SPECIAL:
|
||||
return "The House of the Dead 4 Special";
|
||||
case THE_HOUSE_OF_THE_DEAD_4_SPECIAL_TEST:
|
||||
return "The House of the Dead 4 Special Test Mode";
|
||||
case THE_HOUSE_OF_THE_DEAD_4_TEST:
|
||||
return "The House of the Dead 4 Test Mode";
|
||||
case THE_HOUSE_OF_THE_DEAD_EX:
|
||||
return "The House of the Dead Ex";
|
||||
case TOO_SPICY:
|
||||
return "2 Step : 2 Spicy";
|
||||
case UNKNOWN:
|
||||
return unknownGameTitle;
|
||||
case VIRTUA_FIGHTER_5:
|
||||
return "Virtua Fighter 5";
|
||||
case VIRTUA_FIGHTER_5_FINAL_SHOWDOWN:
|
||||
return "Virtua Fighter 5 Final Showdown";
|
||||
case VIRTUA_FIGHTER_5_FINAL_SHOWDOWN_REVA:
|
||||
return "Virtua Fighter 5 Final Showdown Rev A";
|
||||
case VIRTUA_FIGHTER_5_R:
|
||||
return " Virtua Fighter 5 R";
|
||||
case VIRTUA_FIGHTER_5_REVA:
|
||||
return "Virtua Fighter 5 Rev A";
|
||||
case VIRTUA_FIGHTER_5_REVB:
|
||||
return "Virtua Fighter 5 Rev B";
|
||||
case VIRTUA_FIGHTER_5_REVC:
|
||||
return "Virtua Fighter 5 Rev C";
|
||||
case VIRTUA_FIGHTER_5_R_REVD:
|
||||
return "Virtua Fighter 5 Rev D";
|
||||
case VIRTUA_TENNIS_3:
|
||||
return "Virtua Tennis 3";
|
||||
case VIRTUA_TENNIS_3_TEST:
|
||||
return "Virtua Tennis 3 Test Mode";
|
||||
case THE_HOUSE_OF_THE_DEAD_4_STRIPPED:
|
||||
return "The House of the Dead 4 Rev C";
|
||||
case INITIALD_4_REVE:
|
||||
return "Initial D 4 Exp Rev E";
|
||||
default:
|
||||
return "Unknown Game";
|
||||
return unknownGameTitle;
|
||||
}
|
||||
return "Unknown Game";
|
||||
return unknownGameTitle;
|
||||
}
|
||||
|
||||
int readConfig(FILE *configFile, EmulatorConfig *config)
|
||||
@ -172,6 +365,18 @@ int readConfig(FILE *configFile, EmulatorConfig *config)
|
||||
else if (strcmp(command, "JVS_PATH") == 0)
|
||||
strcpy(config->jvsPath, getNextToken(NULL, " ", &saveptr));
|
||||
|
||||
else if (strcmp(command, "RIDEBOARD_PATH") == 0)
|
||||
strcpy(config->rideboardPath, getNextToken(NULL, " ", &saveptr));
|
||||
|
||||
else if (strcmp(command, "DRIVEBOARD_PATH") == 0)
|
||||
strcpy(config->driveboardPath, getNextToken(NULL, " ", &saveptr));
|
||||
|
||||
else if (strcmp(command, "MOTIONBOARD_PATH") == 0)
|
||||
strcpy(config->motionboardPath, getNextToken(NULL, " ", &saveptr));
|
||||
|
||||
else if (strcmp(command, "FREEPLAY") == 0)
|
||||
config->freeplay = atoi(getNextToken(NULL, " ", &saveptr));
|
||||
|
||||
else if (strcmp(command, "LINDBERGH_COLOUR") == 0)
|
||||
{
|
||||
char colour[256];
|
||||
@ -180,6 +385,21 @@ int readConfig(FILE *configFile, EmulatorConfig *config)
|
||||
config->lindberghColour = RED;
|
||||
}
|
||||
|
||||
else if (strcmp(command, "REGION") == 0)
|
||||
{
|
||||
char region[256];
|
||||
strcpy(region, getNextToken(NULL, " ", &saveptr));
|
||||
if (strcmp(region, "JP") == 0)
|
||||
config->region = JP;
|
||||
else if (strcmp(region, "US") == 0)
|
||||
config->region = US;
|
||||
else
|
||||
config->region = EX;
|
||||
}
|
||||
|
||||
else if (strcmp(command, "DEBUG_MSGS") == 0)
|
||||
config->showDebugMessages = atoi(getNextToken(NULL, " ", &saveptr));
|
||||
|
||||
else
|
||||
printf("Error: Unknown settings command %s\n", command);
|
||||
}
|
||||
@ -201,19 +421,22 @@ int initConfig()
|
||||
strcpy(config.driveboardPath, "none");
|
||||
strcpy(config.motionboardPath, "none");
|
||||
strcpy(config.rideboardPath, "none");
|
||||
config.width = 1024;
|
||||
config.height = 768;
|
||||
|
||||
if (detectGame() != 0)
|
||||
config.width = 640;
|
||||
config.height = 480;
|
||||
config.crc32 = elf_crc;
|
||||
config.region = -1;
|
||||
config.freeplay = -1;
|
||||
config.showDebugMessages = 0;
|
||||
if (detectGame(config.crc32) != 0)
|
||||
{
|
||||
printf("Warning: Unsure what game this is, using default configuration values\n");
|
||||
printf("Warning: Unsure what game with CRC 0x%X is. Please submit this new game to the GitHub repository: https://github.com/bobbydilley/lindbergh-loader/issues/new?title=Please+add+new+game+0x%X&body=I+tried+to+launch+the+following+game:\n", config.crc32, config.crc32);
|
||||
}
|
||||
|
||||
configFile = fopen(CONFIG_PATH, "r");
|
||||
|
||||
if (configFile == NULL)
|
||||
{
|
||||
printf("Warning: Cannot open %s, using default values\n", CONFIG_PATH);
|
||||
printf("Warning: Cannot open %s, using default values.\n", CONFIG_PATH);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -4,18 +4,53 @@
|
||||
|
||||
typedef enum
|
||||
{
|
||||
UNKNOWN,
|
||||
AFTER_BURNER_CLIMAX,
|
||||
AFTER_BURNER_CLIMAX_REVA,
|
||||
AFTER_BURNER_CLIMAX_REVB,
|
||||
AFTER_BURNER_CLIMAX_SDX,
|
||||
AFTER_BURNER_CLIMAX_SDX_REVA,
|
||||
GHOST_SQUAD_EVOLUTION,
|
||||
HARLEY_DAVIDSON,
|
||||
HUMMER,
|
||||
HUMMER_EXTREME,
|
||||
HUMMER_EXTREME_MDX,
|
||||
INITIALD_4,
|
||||
INITIALD_4_REVE,
|
||||
INITIALD_5,
|
||||
LETS_GO_JUNGLE,
|
||||
LETS_GO_JUNGLE_SPECIAL,
|
||||
OUTRUN_2_SP_SDX,
|
||||
OUTRUN_2_SP_SDX_REVA,
|
||||
OUTRUN_2_SP_SDX_REVA_TEST,
|
||||
OUTRUN_2_SP_SDX_TEST,
|
||||
PRIMEVAL_HUNT,
|
||||
RAMBO,
|
||||
RAMBO_CHINA,
|
||||
R_TUNED,
|
||||
SEGABOOT,
|
||||
SEGABOOT_2_4,
|
||||
SEGABOOT_2_4_SYM,
|
||||
SEGABOOT_2_6,
|
||||
SEGA_RACE_TV,
|
||||
THE_HOUSE_OF_THE_DEAD_4,
|
||||
OUTRUN,
|
||||
OUTRUN_TEST,
|
||||
LETS_GO_JUNGLE,
|
||||
ABC,
|
||||
SRTV,
|
||||
RTUNED,
|
||||
VF5
|
||||
THE_HOUSE_OF_THE_DEAD_4_STRIPPED,
|
||||
THE_HOUSE_OF_THE_DEAD_4_SPECIAL,
|
||||
THE_HOUSE_OF_THE_DEAD_4_SPECIAL_TEST,
|
||||
THE_HOUSE_OF_THE_DEAD_4_TEST,
|
||||
THE_HOUSE_OF_THE_DEAD_EX,
|
||||
THE_HOUSE_OF_THE_DEAD_EX_TEST,
|
||||
TOO_SPICY,
|
||||
UNKNOWN,
|
||||
VIRTUA_FIGHTER_5,
|
||||
VIRTUA_FIGHTER_5_FINAL_SHOWDOWN,
|
||||
VIRTUA_FIGHTER_5_FINAL_SHOWDOWN_REVA,
|
||||
VIRTUA_FIGHTER_5_R,
|
||||
VIRTUA_FIGHTER_5_REVA,
|
||||
VIRTUA_FIGHTER_5_REVB,
|
||||
VIRTUA_FIGHTER_5_REVC,
|
||||
VIRTUA_FIGHTER_5_R_REVD,
|
||||
VIRTUA_TENNIS_3,
|
||||
VIRTUA_TENNIS_3_TEST
|
||||
} Game;
|
||||
|
||||
typedef enum
|
||||
@ -24,6 +59,19 @@ typedef enum
|
||||
RED
|
||||
} Colour;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
WORKING,
|
||||
NOT_WORKING
|
||||
} GameStatus;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
JP,
|
||||
US,
|
||||
EX
|
||||
} GameRegion;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int emulateRideboard;
|
||||
@ -41,6 +89,11 @@ typedef struct
|
||||
int height;
|
||||
Game game;
|
||||
Colour lindberghColour;
|
||||
GameStatus gameStatus;
|
||||
uint32_t crc32;
|
||||
GameRegion region;
|
||||
int freeplay;
|
||||
int showDebugMessages;
|
||||
} EmulatorConfig;
|
||||
|
||||
int initConfig();
|
||||
|
@ -3,9 +3,10 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "eeprom.h"
|
||||
#include "eeprom_settings.h"
|
||||
#include "config.h"
|
||||
|
||||
#define I2C_SMBUS_BLOCK_MAX 32
|
||||
#define I2C_SMBUS_BLOCK_MAX 32
|
||||
#define I2C_GET_FUNCTIONS 0x705
|
||||
#define I2C_SMBUS_TRANSFER 0x720
|
||||
#define I2C_SET_SLAVE_MODE 0x703
|
||||
@ -14,17 +15,19 @@
|
||||
#define I2C_SEEK 2
|
||||
#define I2C_WRITE 3
|
||||
|
||||
union i2c_smbus_data {
|
||||
uint8_t byte;
|
||||
uint16_t word;
|
||||
uint8_t block[I2C_SMBUS_BLOCK_MAX + 2];
|
||||
union i2c_smbus_data
|
||||
{
|
||||
uint8_t byte;
|
||||
uint16_t word;
|
||||
uint8_t block[I2C_SMBUS_BLOCK_MAX + 2];
|
||||
};
|
||||
|
||||
struct i2c_smbus_ioctl_data {
|
||||
uint8_t read_write;
|
||||
uint8_t command;
|
||||
uint32_t size;
|
||||
union i2c_smbus_data *data;
|
||||
struct i2c_smbus_ioctl_data
|
||||
{
|
||||
uint8_t read_write;
|
||||
uint8_t command;
|
||||
uint32_t size;
|
||||
union i2c_smbus_data *data;
|
||||
};
|
||||
|
||||
FILE *eeprom = NULL;
|
||||
@ -46,6 +49,35 @@ int initEeprom()
|
||||
|
||||
eeprom = fopen(eepromPath, "rb+");
|
||||
|
||||
if (eepromSettingsInit(eeprom) != 0)
|
||||
{
|
||||
printf("Error initializing eeprom settings.");
|
||||
fclose(eeprom);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (getConfig()->region != -1)
|
||||
{
|
||||
if (getRegion() != getConfig()->region)
|
||||
setRegion(eeprom, getConfig()->region);
|
||||
}
|
||||
|
||||
if (getConfig()->freeplay != -1)
|
||||
{
|
||||
if (getFreeplay() != getConfig()->freeplay)
|
||||
setFreeplay(eeprom, getConfig()->freeplay);
|
||||
}
|
||||
|
||||
if ((getConfig()->game == LETS_GO_JUNGLE_SPECIAL) || (getConfig()->game == THE_HOUSE_OF_THE_DEAD_EX) || (getConfig()->game == THE_HOUSE_OF_THE_DEAD_4_SPECIAL))
|
||||
{
|
||||
if (fixCreditSection(eeprom) != 0)
|
||||
{
|
||||
printf("Error initializing eeprom settings.");
|
||||
fclose(eeprom);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
fseek(eeprom, 0, SEEK_SET);
|
||||
|
||||
return 0;
|
||||
|
345
src/lindbergh/eeprom_settings.c
Normal file
345
src/lindbergh/eeprom_settings.c
Normal file
@ -0,0 +1,345 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "eeprom_settings.h"
|
||||
|
||||
uint32_t crc32_table[255];
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t offset;
|
||||
uint16_t size;
|
||||
} eepromOffsets;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
STATIC,
|
||||
NETWORK_TYPE,
|
||||
ETH0,
|
||||
ETH1,
|
||||
CREDIT,
|
||||
BACKUP
|
||||
} eepromSection;
|
||||
|
||||
eepromOffsets eepromOffsetTable[] = {
|
||||
{0x0000, 0x0020}, // amSysDataRecord_Static
|
||||
{0x00A0, 0x0010}, // amSysDataRecord_NetworkType
|
||||
{0x00B0, 0x0020}, // amSysDataRecord_Eth0
|
||||
{0x00D0, 0x0020}, // amSysDataRecord_Eth1
|
||||
{0x0100, 0x0040}, // amSysDataRecord_Credit
|
||||
{0x0400, 0x0060}}; // amSysDataRecord_Backup
|
||||
|
||||
unsigned char eepromBuffer[512];
|
||||
|
||||
void build_crc32_table()
|
||||
{
|
||||
for (uint32_t i = 0; i < 256; i++)
|
||||
{
|
||||
uint32_t ch = i;
|
||||
uint32_t crc = 0;
|
||||
for (size_t j = 0; j < 8; j++)
|
||||
{
|
||||
uint32_t b = (ch ^ crc) & 1;
|
||||
crc >>= 1;
|
||||
if (b)
|
||||
crc = crc ^ 0xEDB88320;
|
||||
ch >>= 1;
|
||||
}
|
||||
crc32_table[i] = crc;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t gen_crc(int section, size_t n)
|
||||
{
|
||||
unsigned char *buff = &eepromBuffer[eepromOffsetTable[section].offset + 4];
|
||||
unsigned long crc = 0xfffffffful;
|
||||
size_t i;
|
||||
for (i = 0; i < n; i++)
|
||||
crc = crc32_table[*buff++ ^ (crc & 0xff)] ^ (crc >> 8);
|
||||
return (crc);
|
||||
}
|
||||
|
||||
void addCRCtoBuffer(int section)
|
||||
{
|
||||
uint32_t crc = gen_crc(section, eepromOffsetTable[section].size - 4);
|
||||
memcpy(&eepromBuffer[eepromOffsetTable[section].offset], &crc, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
int fillBuffer(FILE *file)
|
||||
{
|
||||
fseek(file, 0, SEEK_SET);
|
||||
int b = fread(eepromBuffer, 1, 512, file);
|
||||
if (b < 512)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int checkCRCinBuffer(int section)
|
||||
{
|
||||
uint32_t crc;
|
||||
memcpy(&crc, &eepromBuffer[eepromOffsetTable[section].offset], 4);
|
||||
if (crc != gen_crc(section, eepromOffsetTable[section].size - 4))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int createStaticSection()
|
||||
{
|
||||
unsigned char *buff = &eepromBuffer[eepromOffsetTable[STATIC].offset];
|
||||
memset(buff, 0, eepromOffsetTable[STATIC].size);
|
||||
buff[14] = 0;
|
||||
memcpy(buff + 15, "AAGX-01A00009999", 16);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int createNetworkTypeSection()
|
||||
{
|
||||
memset(&eepromBuffer[eepromOffsetTable[NETWORK_TYPE].offset], 0,
|
||||
eepromOffsetTable[NETWORK_TYPE].size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int createEthSection(int section)
|
||||
{
|
||||
unsigned char *buff = &eepromBuffer[eepromOffsetTable[section].offset];
|
||||
memset(buff, 0, eepromOffsetTable[section].size);
|
||||
uint32_t value = 1;
|
||||
memcpy(buff + 8, &value, sizeof(uint32_t));
|
||||
value = inet_addr("10.0.0.1");
|
||||
if (section == ETH1)
|
||||
value += (1 << 24);
|
||||
memcpy(buff + 12, &value, sizeof(uint32_t));
|
||||
value = inet_addr("255.255.255.0");
|
||||
memcpy(buff + 16, &value, sizeof(uint32_t));
|
||||
value = inet_addr("0.0.0.0");
|
||||
memcpy(buff + 20, &value, sizeof(uint32_t));
|
||||
memcpy(buff + 24, &value, sizeof(uint32_t));
|
||||
memcpy(buff + 28, &value, sizeof(uint32_t));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int createCreditSection()
|
||||
{
|
||||
unsigned char *buff = &eepromBuffer[eepromOffsetTable[CREDIT].offset];
|
||||
memset(buff, 0, eepromOffsetTable[CREDIT].size);
|
||||
buff[32] = 99;
|
||||
buff[33] = 9;
|
||||
buff[34] = 4;
|
||||
buff[35] = 0; // Coin chute type [COMMON (Default) / INDIVIDUAL]
|
||||
buff[36] = 1; // Service Type [COMMON / INDIVIDUAL (Default)]
|
||||
buff[38] = 1; //
|
||||
buff[39] = 0; // Freeplay set to 1
|
||||
buff[40] = 1; // Credits Chute #1
|
||||
buff[41] = 1; // Credits Chute #1
|
||||
buff[42] = 0; //
|
||||
buff[43] = 1; // Coins
|
||||
for (size_t i = 0; i < 8; i++)
|
||||
{
|
||||
buff[44 + i] = 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int writeSectiontoFile(FILE *file, int section)
|
||||
{
|
||||
unsigned char *buff = &eepromBuffer[eepromOffsetTable[section].offset];
|
||||
// Original
|
||||
if (fseek(file, eepromOffsetTable[section].offset, SEEK_SET) != 0)
|
||||
return 1;
|
||||
if (fwrite(buff, eepromOffsetTable[section].size, 1, file) != 1)
|
||||
return 1;
|
||||
|
||||
// Duplicate
|
||||
if (fseek(file, eepromOffsetTable[section].offset + 0x200, SEEK_SET) != 0)
|
||||
return 1;
|
||||
if (fwrite(buff, eepromOffsetTable[section].size, 1, file) != 1)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int getRegion()
|
||||
{
|
||||
return eepromBuffer[14];
|
||||
}
|
||||
|
||||
int setRegion(FILE *eeprom, int region)
|
||||
{
|
||||
eepromBuffer[14] = region;
|
||||
addCRCtoBuffer(STATIC);
|
||||
if (writeSectiontoFile(eeprom, STATIC) != 0)
|
||||
{
|
||||
printf("Error writing to eeprom.");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int getFreeplay()
|
||||
{
|
||||
return eepromBuffer[eepromOffsetTable[CREDIT].offset + 39];
|
||||
}
|
||||
|
||||
int setFreeplay(FILE *eeprom, int freeplay)
|
||||
{
|
||||
if (createCreditSection() != 0)
|
||||
{
|
||||
printf("Error setting Free Play.");
|
||||
return 1;
|
||||
}
|
||||
eepromBuffer[eepromOffsetTable[CREDIT].offset + 39] = freeplay;
|
||||
addCRCtoBuffer(CREDIT);
|
||||
if (writeSectiontoFile(eeprom, CREDIT) != 0)
|
||||
{
|
||||
printf("Error writing to eeprom.");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fixCreditSection(FILE *eeprom)
|
||||
{
|
||||
eepromBuffer[eepromOffsetTable[CREDIT].offset + 36] = 0;
|
||||
eepromBuffer[eepromOffsetTable[CREDIT].offset + 39] = 0;
|
||||
addCRCtoBuffer(CREDIT);
|
||||
if (writeSectiontoFile(eeprom, CREDIT) != 0)
|
||||
{
|
||||
printf("Error writing to eeprom.");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int eepromSettingsInit( FILE *eeprom)
|
||||
{
|
||||
build_crc32_table();
|
||||
|
||||
eeprom = fopen("eeprom.bin", "r+b");
|
||||
if (eeprom == NULL)
|
||||
{
|
||||
printf("eeprom.bin cannot be opened, let's create a new one.\n");
|
||||
eeprom = fopen("eeprom.bin", "w+b");
|
||||
}
|
||||
fseek(eeprom, 0, SEEK_END);
|
||||
int size = ftell(eeprom);
|
||||
fseek(eeprom, 0, SEEK_SET);
|
||||
if (size >= 832) // eeprom initialized at least by SEGABOOT
|
||||
{
|
||||
fillBuffer(eeprom); // Fills buffer with 1st 512 bytes of the eeprom file
|
||||
if (checkCRCinBuffer(STATIC) != 0)
|
||||
{
|
||||
// Create section from scratch
|
||||
if (createStaticSection() != 0)
|
||||
{
|
||||
printf("Error creating Static Section.\n");
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
addCRCtoBuffer(STATIC);
|
||||
if (writeSectiontoFile(eeprom, STATIC) != 0)
|
||||
{
|
||||
printf("Error writing to eeprom.bin [STATIC].");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (checkCRCinBuffer(NETWORK_TYPE) != 0)
|
||||
{
|
||||
// Create section from scratch
|
||||
if (createNetworkTypeSection() != 0)
|
||||
{
|
||||
printf("Error creating NetworkType Section.\n");
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
addCRCtoBuffer(NETWORK_TYPE);
|
||||
if (writeSectiontoFile(eeprom, NETWORK_TYPE) != 0)
|
||||
{
|
||||
printf("Error writing to eeprom.bin [NETWORK_TYPE].");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (checkCRCinBuffer(ETH0) != 0)
|
||||
{
|
||||
// Create section from scratch
|
||||
if (createEthSection(ETH0) != 0)
|
||||
{
|
||||
printf("Error creating Eth0 Section.\n");
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
addCRCtoBuffer(ETH0);
|
||||
if (writeSectiontoFile(eeprom, ETH0) != 0)
|
||||
{
|
||||
printf("Error writing to eeprom.bin [ETH0].");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (checkCRCinBuffer(ETH1) != 0)
|
||||
{
|
||||
// Create section from scratch
|
||||
if (createEthSection(ETH1) != 0)
|
||||
{
|
||||
printf("Error creating Eth0 Section.\n");
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
addCRCtoBuffer(ETH1);
|
||||
if (writeSectiontoFile(eeprom, ETH1) != 0)
|
||||
{
|
||||
printf("Error writing to eeprom.bin [ETH1].");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (checkCRCinBuffer(CREDIT) != 0)
|
||||
{
|
||||
// Create section from scratch
|
||||
if (createCreditSection() != 0)
|
||||
{
|
||||
printf("Error creating CREDIT Section.\n");
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
addCRCtoBuffer(CREDIT);
|
||||
if (writeSectiontoFile(eeprom, NETWORK_TYPE) != 0)
|
||||
{
|
||||
printf("Error writing to eeprom.bin [CREDIT]].");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create all sections from scratch
|
||||
if ((createStaticSection() != 0) || (createNetworkTypeSection() != 0) ||
|
||||
(createEthSection(ETH0) != 0) || (createEthSection(ETH1) != 0) || (createCreditSection() != 0))
|
||||
{
|
||||
printf("Error creating section from scratch.\n");
|
||||
return 1;
|
||||
}
|
||||
addCRCtoBuffer(STATIC);
|
||||
addCRCtoBuffer(NETWORK_TYPE);
|
||||
addCRCtoBuffer(ETH0);
|
||||
addCRCtoBuffer(ETH1);
|
||||
addCRCtoBuffer(CREDIT);
|
||||
if ((writeSectiontoFile(eeprom, STATIC) != 0) || (writeSectiontoFile(eeprom, NETWORK_TYPE) != 0) ||
|
||||
(writeSectiontoFile(eeprom, ETH0) != 0) || (writeSectiontoFile(eeprom, ETH1) != 0) ||
|
||||
(writeSectiontoFile(eeprom, CREDIT) != 0))
|
||||
{
|
||||
printf("Error writing section from scratch.\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
6
src/lindbergh/eeprom_settings.h
Normal file
6
src/lindbergh/eeprom_settings.h
Normal file
@ -0,0 +1,6 @@
|
||||
int eepromSettingsInit(FILE *eeprom);
|
||||
int getRegion();
|
||||
int getFreeplay();
|
||||
int setRegion(FILE *eeprom, int region);
|
||||
int setFreeplay(FILE *eeprom, int freeplay);
|
||||
int fixCreditSection(FILE *eeprom);
|
@ -1,3 +1,4 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <GL/freeglut.h>
|
||||
#include <GL/glx.h>
|
||||
#include <X11/extensions/xf86vmode.h>
|
||||
@ -25,11 +26,11 @@ FGAPI int FGAPIENTRY glutEnterGameMode()
|
||||
{
|
||||
char gameTitle[256] = {0};
|
||||
strcat(gameTitle, getGameName());
|
||||
strcat(gameTitle, " (GLUT)");
|
||||
glutCreateWindow(gameTitle);
|
||||
|
||||
// Outrun doesn't run the glutMainLoop through, so we'll do that here
|
||||
if (getConfig()->game == OUTRUN || getConfig()->game == OUTRUN_TEST)
|
||||
Game game = getConfig()->game;
|
||||
if (game == OUTRUN_2_SP_SDX || game == OUTRUN_2_SP_SDX_TEST || game == OUTRUN_2_SP_SDX_REVA || game == OUTRUN_2_SP_SDX_REVA_TEST)
|
||||
{
|
||||
pthread_t glutMainLoopID;
|
||||
pthread_create(&glutMainLoopID, NULL, &glutMainLoopThread, NULL);
|
||||
@ -51,7 +52,7 @@ FGAPI void FGAPIENTRY glutSetCursor(int cursor)
|
||||
|
||||
FGAPI void FGAPIENTRY glutGameModeString(const char *string)
|
||||
{
|
||||
printf("glutGameModeString: %s\n", string);
|
||||
// printf("glutGameModeString: %s\n", string);
|
||||
|
||||
char gameModeString[1024];
|
||||
strcpy(gameModeString, string);
|
||||
@ -85,6 +86,14 @@ FGAPI void FGAPIENTRY glutGameModeString(const char *string)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the house of the dead games turning keyboard repeating off.
|
||||
*/
|
||||
int XAutoRepeatOff(Display *display)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
Window XCreateWindow(Display *display, Window parent, int x, int y, unsigned int width, unsigned int height, unsigned int border_width, int depth, unsigned int class, Visual *visual, unsigned long valueMask, XSetWindowAttributes *attributes)
|
||||
{
|
||||
|
||||
@ -98,7 +107,7 @@ Window XCreateWindow(Display *display, Window parent, int x, int y, unsigned int
|
||||
// attributes->override_redirect = False;
|
||||
|
||||
Window window = _XCreateWindow(display, parent, x, y, width, height, border_width, depth, class, visual, valueMask, attributes);
|
||||
printf("XCreateWindow Resolution %d %d %d %d\n", x, y, width, height);
|
||||
printf("The resolution is %dx%d \n", width, height);
|
||||
|
||||
if (getConfig()->fullscreen)
|
||||
{
|
||||
@ -136,120 +145,40 @@ int XStoreName(Display *display, Window w, const char *window_name)
|
||||
int (*_XStoreName)(Display *display, Window w, const char *window_name) = dlsym(RTLD_NEXT, "XStoreName");
|
||||
char gameTitle[256] = {0};
|
||||
strcat(gameTitle, getGameName());
|
||||
strcat(gameTitle, " (X11)");
|
||||
return _XStoreName(display, w, gameTitle);
|
||||
}
|
||||
|
||||
int XNextEvent(Display *display, XEvent *event_return)
|
||||
{
|
||||
|
||||
int (*_XNextEvent)(Display *display, XEvent *event_return) = dlsym(RTLD_NEXT, "XNextEvent");
|
||||
int returnValue = _XNextEvent(display, event_return);
|
||||
|
||||
// Return now if we're not emulating JVS
|
||||
if (!getConfig()->emulateJVS)
|
||||
{
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
switch (event_return->type)
|
||||
{
|
||||
|
||||
case KeyRelease:
|
||||
case KeyPress:
|
||||
{
|
||||
switch (event_return->xkey.keycode)
|
||||
{
|
||||
case 28:
|
||||
setSwitch(SYSTEM, BUTTON_TEST, event_return->type == KeyPress);
|
||||
break;
|
||||
case 39:
|
||||
setSwitch(PLAYER_1, BUTTON_SERVICE, event_return->type == KeyPress);
|
||||
break;
|
||||
case 14:
|
||||
incrementCoin(PLAYER_1, event_return->type == KeyPress);
|
||||
break;
|
||||
case 15:
|
||||
incrementCoin(PLAYER_2, event_return->type == KeyPress);
|
||||
break;
|
||||
case 111:
|
||||
setSwitch(PLAYER_1, BUTTON_UP, event_return->type == KeyPress);
|
||||
break;
|
||||
case 116:
|
||||
setSwitch(PLAYER_1, BUTTON_DOWN, event_return->type == KeyPress);
|
||||
break;
|
||||
case 113:
|
||||
setSwitch(PLAYER_1, BUTTON_LEFT, event_return->type == KeyPress);
|
||||
break;
|
||||
case 114:
|
||||
setSwitch(PLAYER_1, BUTTON_RIGHT, event_return->type == KeyPress);
|
||||
break;
|
||||
case 10:
|
||||
setSwitch(PLAYER_1, BUTTON_START, event_return->type == KeyPress);
|
||||
break;
|
||||
case 24:
|
||||
setSwitch(PLAYER_1, BUTTON_1, event_return->type == KeyPress);
|
||||
break;
|
||||
case 25:
|
||||
setSwitch(PLAYER_1, BUTTON_2, event_return->type == KeyPress);
|
||||
break;
|
||||
case 26:
|
||||
setSwitch(PLAYER_1, BUTTON_3, event_return->type == KeyPress);
|
||||
break;
|
||||
case 27:
|
||||
setSwitch(PLAYER_1, BUTTON_4, event_return->type == KeyPress);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MotionNotify:
|
||||
{
|
||||
setAnalogue(ANALOGUE_1, ((double)event_return->xmotion.x / (double)getConfig()->width) * 255);
|
||||
setAnalogue(ANALOGUE_2, ((double)event_return->xmotion.y / (double)getConfig()->height) * 255);
|
||||
}
|
||||
break;
|
||||
|
||||
case ButtonPress:
|
||||
case ButtonRelease:
|
||||
{
|
||||
switch (event_return->xbutton.button)
|
||||
{
|
||||
case 1: // Trigger
|
||||
setSwitch(PLAYER_1, BUTTON_1, event_return->type == ButtonPress);
|
||||
break;
|
||||
case 3: // Reload
|
||||
setSwitch(PLAYER_1, BUTTON_2, event_return->type == ButtonPress);
|
||||
break;
|
||||
case 9: // Gun Button
|
||||
setSwitch(PLAYER_1, BUTTON_3, event_return->type == ButtonPress);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
int XSetStandardProperties(Display *display, Window window, const char *window_name, const char *icon_name, Pixmap icon_pixmap, char **argv, int argc, XSizeHints *hints)
|
||||
{
|
||||
int (*_XSetStandardProperties)(Display *display, Window window, const char *window_name, const char *icon_name, Pixmap icon_pixmap, char **argv, int argc, XSizeHints *hints) = dlsym(RTLD_NEXT, "XSetStandardProperties");
|
||||
char gameTitle[256] = {0};
|
||||
strcat(gameTitle, getGameName());
|
||||
strcat(gameTitle, " (X11)");
|
||||
return _XSetStandardProperties(display, window, gameTitle, icon_name, icon_pixmap, argv, argc, hints);
|
||||
}
|
||||
|
||||
Bool XF86VidModeSwitchToMode(Display *display, int screen, XF86VidModeModeInfo *modeline)
|
||||
Bool XF86VidModeSwitchToMode(Display *display, int screen, XF86VidModeModeInfo *modesinfo)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int XF86VidModeGetAllModeLines(Display *display, int screen, int *modecount_return, XF86VidModeModeInfo ***modesinfo)
|
||||
{
|
||||
int (*_XF86VidModeGetAllModeLines)(Display *display, int screen, int *modecount_return, XF86VidModeModeInfo ***modesinfo) = dlsym(RTLD_NEXT, "XF86VidModeGetAllModeLines");
|
||||
|
||||
if (_XF86VidModeGetAllModeLines(display, screen, modecount_return, modesinfo) != 1)
|
||||
{
|
||||
printf("Error: Could not get list of screen modes.\n");
|
||||
exit(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
XF86VidModeModeInfo **modes = *modesinfo;
|
||||
modes[0]->hdisplay = getConfig()->width;
|
||||
modes[0]->vdisplay = getConfig()->height;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef unsigned int uint;
|
||||
|
||||
int glXSwapIntervalSGI(int interval)
|
||||
|
235
src/lindbergh/hook.c
Normal file → Executable file
235
src/lindbergh/hook.c
Normal file → Executable file
@ -1,3 +1,5 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <link.h>
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <linux/sockios.h>
|
||||
@ -13,6 +15,8 @@
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <cpuid.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "hook.h"
|
||||
|
||||
@ -24,6 +28,8 @@
|
||||
#include "rideboard.h"
|
||||
#include "securityboard.h"
|
||||
#include "patch.h"
|
||||
#include "pcidata.h"
|
||||
#include "input.h"
|
||||
|
||||
#define HOOK_FILE_NAME "/dev/zero"
|
||||
|
||||
@ -34,23 +40,38 @@
|
||||
|
||||
#define CPUINFO 0
|
||||
#define OSRELEASE 1
|
||||
#define PCI_CARD_1F0 2
|
||||
|
||||
int hooks[5] = {-1, -1, -1, -1};
|
||||
FILE *fileHooks[2] = {NULL, NULL};
|
||||
int fileRead[2] = {0, 0};
|
||||
FILE *fileHooks[3] = {NULL, NULL, NULL};
|
||||
int fileRead[3] = {0, 0, 0};
|
||||
char envpath[100];
|
||||
uint32_t elf_crc = 0;
|
||||
|
||||
cpuvendor cpu_vendor = {0};
|
||||
|
||||
static int callback(struct dl_phdr_info *info, size_t size, void *data);
|
||||
|
||||
uint16_t basePortAddress = 0xFFFF;
|
||||
|
||||
/**
|
||||
* Signal handler for the SIGSEGV signal, which is triggered when a process tries to access an illegal memory location.
|
||||
* @param signal
|
||||
* @param info
|
||||
* @param ptr
|
||||
*/
|
||||
static void handleSegfault(int signal, siginfo_t *info, void *ptr)
|
||||
{
|
||||
ucontext_t *ctx = ptr;
|
||||
|
||||
// Get the address of the instruction causing the segfault
|
||||
uint8_t *code = (uint8_t *)ctx->uc_mcontext.gregs[REG_EIP];
|
||||
|
||||
switch (*code)
|
||||
{
|
||||
case 0xED:
|
||||
{
|
||||
// Get the port number from the EDX register
|
||||
uint16_t port = ctx->uc_mcontext.gregs[REG_EDX] & 0xFFFF;
|
||||
|
||||
// The first port called is usually random, but everything after that
|
||||
@ -60,9 +81,11 @@ static void handleSegfault(int signal, siginfo_t *info, void *ptr)
|
||||
if (basePortAddress == 0xFFFF)
|
||||
basePortAddress = port;
|
||||
|
||||
// Adjust the port number if necessary
|
||||
if (port > 0x38)
|
||||
port = port - basePortAddress;
|
||||
|
||||
// Call the security board input function with the port number and data
|
||||
securityBoardIn(port, (uint32_t *)&(ctx->uc_mcontext.gregs[REG_EAX]));
|
||||
|
||||
ctx->uc_mcontext.gregs[REG_EIP]++;
|
||||
@ -70,15 +93,17 @@ static void handleSegfault(int signal, siginfo_t *info, void *ptr)
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xE7: // OUT IMMIDIATE
|
||||
case 0xE7: // OUT IMMEDIATE
|
||||
{
|
||||
// Increment the instruction pointer by two to skip over this instruction
|
||||
ctx->uc_mcontext.gregs[REG_EIP] += 2;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xE6: // OUT IMMIDIATE
|
||||
case 0xE6: // OUT IMMEDIATE
|
||||
{
|
||||
// Increment the instruction pointer by two to skip over this instruction
|
||||
ctx->uc_mcontext.gregs[REG_EIP] += 2;
|
||||
return;
|
||||
}
|
||||
@ -110,7 +135,13 @@ static void handleSegfault(int signal, siginfo_t *info, void *ptr)
|
||||
|
||||
void __attribute__((constructor)) hook_init()
|
||||
{
|
||||
printf("SEGA Lindbergh Loader\nRobert Dilley 2022\nNot for public consumption\n\n");
|
||||
printf("SEGA Lindbergh Loader\nRobert Dilley 2023\nNot for public consumption\n\n");
|
||||
|
||||
// Get offsets of the Game's ELF and calculate CRC32.
|
||||
dl_iterate_phdr(callback, NULL);
|
||||
|
||||
// Get CPU ID
|
||||
getCPUID();
|
||||
|
||||
// Implement SIGSEGV handler
|
||||
struct sigaction act;
|
||||
@ -147,9 +178,20 @@ void __attribute__((constructor)) hook_init()
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (initInput() != 0)
|
||||
exit(1);
|
||||
|
||||
securityBoardSetDipResolution(getConfig()->width, getConfig()->height);
|
||||
|
||||
printf("Now emulating %s\n", getGameName());
|
||||
printf("Now starting \"%s\"", getGameName());
|
||||
if (getConfig()->gameStatus == WORKING)
|
||||
{
|
||||
printf((", this game is working.\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
printf((", this game is NOT working.\n"));
|
||||
}
|
||||
}
|
||||
|
||||
int open(const char *pathname, int flags)
|
||||
@ -161,14 +203,12 @@ int open(const char *pathname, int flags)
|
||||
if (strcmp(pathname, "/dev/lbb") == 0)
|
||||
{
|
||||
hooks[BASEBOARD] = _open(HOOK_FILE_NAME, flags);
|
||||
printf("Baseboard opened %d\n", hooks[BASEBOARD]);
|
||||
return hooks[BASEBOARD];
|
||||
}
|
||||
|
||||
if (strcmp(pathname, "/dev/i2c/0") == 0)
|
||||
{
|
||||
hooks[EEPROM] = _open(HOOK_FILE_NAME, flags);
|
||||
printf("EEPROM opened %d\n", hooks[EEPROM]);
|
||||
return hooks[EEPROM];
|
||||
}
|
||||
|
||||
@ -178,7 +218,7 @@ int open(const char *pathname, int flags)
|
||||
return -1;
|
||||
|
||||
hooks[SERIAL0] = _open(HOOK_FILE_NAME, flags);
|
||||
printf("SERIAL0 Opened %d\n", hooks[SERIAL0]);
|
||||
printf("Warning: SERIAL0 Opened %d\n", hooks[SERIAL0]);
|
||||
return hooks[SERIAL0];
|
||||
}
|
||||
|
||||
@ -188,7 +228,7 @@ int open(const char *pathname, int flags)
|
||||
return -1;
|
||||
|
||||
hooks[SERIAL1] = _open(HOOK_FILE_NAME, flags);
|
||||
printf("SERIAL1 opened %d\n", hooks[SERIAL1]);
|
||||
printf("Warning: SERIAL1 opened %d\n", hooks[SERIAL1]);
|
||||
return hooks[SERIAL1];
|
||||
}
|
||||
|
||||
@ -206,18 +246,30 @@ int open64(const char *pathname, int flags)
|
||||
return open(pathname, flags);
|
||||
}
|
||||
|
||||
|
||||
|
||||
FILE *fopen(const char *restrict pathname, const char *restrict mode)
|
||||
{
|
||||
FILE *(*_fopen)(const char *restrict pathname, const char *restrict mode) = dlsym(RTLD_NEXT, "fopen");
|
||||
// printf("fopen %s\n", pathname);
|
||||
|
||||
if (strcmp(pathname, "/root/lindbergrc") == 0)
|
||||
{
|
||||
return _fopen("lindbergrc", mode);
|
||||
}
|
||||
|
||||
if ((strcmp(pathname, "/usr/lib/boot/logo.tga") == 0) || (strcmp(pathname, "/usr/lib/boot/logo.tga") == 0))
|
||||
{
|
||||
return _fopen("logo.tga", mode);
|
||||
}
|
||||
|
||||
if (strcmp(pathname, "/usr/lib/boot/LucidaConsole_12.tga") == 0)
|
||||
{
|
||||
return _fopen("LucidaConsole_12.tga", mode);
|
||||
}
|
||||
|
||||
if (strcmp(pathname, "/usr/lib/boot/LucidaConsole_12.abc") == 0)
|
||||
{
|
||||
return _fopen("LucidaConsole_12.abc", mode);
|
||||
}
|
||||
|
||||
if (strcmp(pathname, "/proc/cpuinfo") == 0)
|
||||
{
|
||||
fileRead[CPUINFO] = 0;
|
||||
@ -225,6 +277,26 @@ FILE *fopen(const char *restrict pathname, const char *restrict mode)
|
||||
return fileHooks[CPUINFO];
|
||||
}
|
||||
|
||||
if (strcmp(pathname, "/usr/lib/boot/logo_red.tga") == 0)
|
||||
{
|
||||
return _fopen("logo_red.tga", mode);
|
||||
}
|
||||
if (strcmp(pathname, "/usr/lib/boot/SEGA_KakuGothic-DB-Roman_12.tga") == 0)
|
||||
{
|
||||
return _fopen("SEGA_KakuGothic-DB-Roman_12.tga", mode);
|
||||
}
|
||||
|
||||
if (strcmp(pathname, "/usr/lib/boot/SEGA_KakuGothic-DB-Roman_12.abc") == 0)
|
||||
{
|
||||
return _fopen("SEGA_KakuGothic-DB-Roman_12.abc", mode);
|
||||
}
|
||||
|
||||
if (strcmp(pathname, "/proc/bus/pci/00/1f.0") == 0)
|
||||
{
|
||||
fileRead[PCI_CARD_1F0] = 0;
|
||||
fileHooks[PCI_CARD_1F0] = _fopen(HOOK_FILE_NAME, mode);
|
||||
return fileHooks[PCI_CARD_1F0];
|
||||
}
|
||||
return _fopen(pathname, mode);
|
||||
}
|
||||
|
||||
@ -242,9 +314,42 @@ FILE *fopen64(const char *pathname, const char *mode)
|
||||
return fileHooks[OSRELEASE];
|
||||
}
|
||||
|
||||
if (strcmp(pathname, "/usr/lib/boot/logo_red.tga") == 0)
|
||||
{
|
||||
return _fopen64("logo_red.tga", mode);
|
||||
}
|
||||
|
||||
if (strcmp(pathname, "/usr/lib/boot/logo.tga") == 0)
|
||||
{
|
||||
return _fopen64("logo.tga", mode);
|
||||
}
|
||||
|
||||
if (strcmp(pathname, "/usr/lib/boot/SEGA_KakuGothic-DB-Roman_12.tga") == 0)
|
||||
{
|
||||
return _fopen64("SEGA_KakuGothic-DB-Roman_12.tga", mode);
|
||||
}
|
||||
|
||||
if (strcmp(pathname, "/usr/lib/boot/SEGA_KakuGothic-DB-Roman_12.abc") == 0)
|
||||
{
|
||||
return _fopen64("SEGA_KakuGothic-DB-Roman_12.abc", mode);
|
||||
}
|
||||
return _fopen64(pathname, mode);
|
||||
}
|
||||
|
||||
int fclose(FILE *stream)
|
||||
{
|
||||
int (*_fclose)(FILE *stream) = dlsym(RTLD_NEXT, "fclose");
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
if (fileHooks[i] == stream)
|
||||
{
|
||||
int r = _fclose(stream);
|
||||
fileHooks[i] = NULL;
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return _fclose(stream);
|
||||
}
|
||||
int openat(int dirfd, const char *pathname, int flags)
|
||||
{
|
||||
int (*_openat)(int dirfd, const char *pathname, int flags) = dlsym(RTLD_NEXT, "openat");
|
||||
@ -336,6 +441,18 @@ ssize_t read(int fd, void *buf, size_t count)
|
||||
return _read(fd, buf, count);
|
||||
}
|
||||
|
||||
size_t fread(void *buf, size_t size, size_t count, FILE *stream)
|
||||
{
|
||||
size_t (*_fread)(void *buf, size_t size, size_t count, FILE *stream) = dlsym(RTLD_NEXT, "fread");
|
||||
|
||||
if (stream == fileHooks[PCI_CARD_1F0])
|
||||
{
|
||||
memcpy(buf, pcidata, 68);
|
||||
return 68;
|
||||
}
|
||||
return _fread(buf, size, count, stream);
|
||||
}
|
||||
|
||||
ssize_t write(int fd, const void *buf, size_t count)
|
||||
{
|
||||
int (*_write)(int fd, const void *buf, size_t count) = dlsym(RTLD_NEXT, "write");
|
||||
@ -428,15 +545,14 @@ int system(const char *command)
|
||||
return 0;
|
||||
|
||||
if (strcmp(command, "uname -r | grep mvl") == 0)
|
||||
{
|
||||
EmulatorConfig *config = getConfig();
|
||||
config->game = SEGABOOT_2_4;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strstr(command, "hwclock") != NULL)
|
||||
return 0;
|
||||
|
||||
if (strstr(command, "losetup") != NULL)
|
||||
return 0;
|
||||
|
||||
return _system(command);
|
||||
}
|
||||
|
||||
@ -465,7 +581,7 @@ float powf(float base, float exponent)
|
||||
return (float)pow((double)base, (double)exponent);
|
||||
}
|
||||
|
||||
/** This might be required for some games
|
||||
/*
|
||||
int sem_wait(sem_t *sem)
|
||||
{
|
||||
int (*original_sem_wait)(sem_t * sem) = dlsym(RTLD_NEXT, "sem_wait");
|
||||
@ -476,6 +592,10 @@ int sem_wait(sem_t *sem)
|
||||
/**
|
||||
* Hook function used by Harley Davidson to change IPs to localhost
|
||||
* Currently does nothing.
|
||||
* @param sockfd
|
||||
* @param addr
|
||||
* @param addrlen
|
||||
* @return
|
||||
*/
|
||||
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
|
||||
{
|
||||
@ -486,11 +606,60 @@ int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
|
||||
// Change the IP to connect to to 127.0.0.1
|
||||
// in_pointer->sin_addr.s_addr = inet_addr("127.0.0.1");
|
||||
char *some_addr = inet_ntoa(in_pointer->sin_addr);
|
||||
printf("Connecting to %s\n", some_addr);
|
||||
if (getConfig()->showDebugMessages)
|
||||
{
|
||||
printf("Connecting to %s\n", some_addr);
|
||||
}
|
||||
|
||||
return _connect(sockfd, addr, addrlen);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to calculate CRC32 checksum in memory.
|
||||
*/
|
||||
uint32_t get_crc32(const char *s, size_t n)
|
||||
{
|
||||
uint32_t crc = 0xFFFFFFFF;
|
||||
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
char ch = s[i];
|
||||
for (size_t j = 0; j < 8; j++)
|
||||
{
|
||||
uint32_t b = (ch ^ crc) & 1;
|
||||
crc >>= 1;
|
||||
if (b)
|
||||
crc = crc ^ 0xEDB88320;
|
||||
ch >>= 1;
|
||||
}
|
||||
}
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function to get the offset and size of the execution program in memory of the ELF we hook to.
|
||||
*/
|
||||
static int callback(struct dl_phdr_info *info, size_t size, void *data)
|
||||
{
|
||||
if ((info->dlpi_phnum >= 3) && (info->dlpi_phdr[2].p_type == PT_LOAD) && (info->dlpi_phdr[2].p_flags == 5))
|
||||
{
|
||||
elf_crc = get_crc32((void *)(info->dlpi_addr + info->dlpi_phdr[2].p_vaddr + 10), 128);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void getCPUID()
|
||||
{
|
||||
unsigned eax;
|
||||
eax = 0;
|
||||
__get_cpuid(0, &eax, &cpu_vendor.ebx, &cpu_vendor.ecx, &cpu_vendor.edx);
|
||||
sprintf(cpu_vendor.cpuid, "%.4s%.4s%.4s", (const char *)&cpu_vendor.ebx, (const char *)&cpu_vendor.edx, (const char *)&cpu_vendor.ecx);
|
||||
if (getConfig()->showDebugMessages)
|
||||
{
|
||||
printf("Detected CPU Vendor: %s\n", cpu_vendor.cpuid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the game changing the DISPLAY environment variable
|
||||
*/
|
||||
@ -506,6 +675,34 @@ int setenv(const char *name, const char *value, int overwrite)
|
||||
return _setenv(name, value, overwrite);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fake the TEA_DIR environment variable to games that require it to run
|
||||
*/
|
||||
char *getenv(const char *name)
|
||||
{
|
||||
char *(*_getenv)(const char *name) = dlsym(RTLD_NEXT, "getenv");
|
||||
|
||||
if ((strcmp(name, "TEA_DIR") == 0) && ((getConfig()->game == VIRTUA_TENNIS_3) || (getConfig()->game == VIRTUA_TENNIS_3_TEST) ||
|
||||
((getConfig()->game == RAMBO)) || (getConfig()->game == TOO_SPICY)))
|
||||
{
|
||||
if (getcwd(envpath, 100) == NULL)
|
||||
return "";
|
||||
char *ptr = strrchr(envpath, '/');
|
||||
if (ptr == NULL)
|
||||
return "";
|
||||
*ptr = '\0';
|
||||
return envpath;
|
||||
}
|
||||
else if (strcmp(name, "TEA_DIR") == 0)
|
||||
{
|
||||
if (getcwd(envpath, 100) == NULL)
|
||||
return "";
|
||||
return envpath;
|
||||
}
|
||||
|
||||
return _getenv(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the game unsetting the DISPLAY environment variable
|
||||
*/
|
||||
|
@ -0,0 +1,10 @@
|
||||
|
||||
uint32_t get_crc32(const char *s,size_t n);
|
||||
void getCPUID();
|
||||
typedef struct
|
||||
{
|
||||
unsigned ebx;
|
||||
unsigned edx;
|
||||
unsigned ecx;
|
||||
char cpuid[20];
|
||||
}cpuvendor;
|
118
src/lindbergh/input.c
Normal file
118
src/lindbergh/input.c
Normal file
@ -0,0 +1,118 @@
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include "jvs.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <GL/freeglut.h>
|
||||
#include <GL/glx.h>
|
||||
#include <X11/extensions/xf86vmode.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <pthread.h>
|
||||
#include <math.h>
|
||||
|
||||
|
||||
int initInput()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int XNextEvent(Display *display, XEvent *event_return)
|
||||
{
|
||||
|
||||
int (*_XNextEvent)(Display *display, XEvent *event_return) = dlsym(RTLD_NEXT, "XNextEvent");
|
||||
int returnValue = _XNextEvent(display, event_return);
|
||||
|
||||
// Return now if we're not emulating JVS
|
||||
if (!getConfig()->emulateJVS)
|
||||
{
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
switch (event_return->type)
|
||||
{
|
||||
|
||||
case KeyRelease:
|
||||
case KeyPress:
|
||||
{
|
||||
switch (event_return->xkey.keycode)
|
||||
{
|
||||
case 28:
|
||||
setSwitch(SYSTEM, BUTTON_TEST, event_return->type == KeyPress);
|
||||
break;
|
||||
case 39:
|
||||
setSwitch(PLAYER_1, BUTTON_SERVICE, event_return->type == KeyPress);
|
||||
break;
|
||||
case 14:
|
||||
incrementCoin(PLAYER_1, event_return->type == KeyPress);
|
||||
break;
|
||||
case 15:
|
||||
incrementCoin(PLAYER_2, event_return->type == KeyPress);
|
||||
break;
|
||||
case 111:
|
||||
setSwitch(PLAYER_1, BUTTON_UP, event_return->type == KeyPress);
|
||||
break;
|
||||
case 116:
|
||||
setSwitch(PLAYER_1, BUTTON_DOWN, event_return->type == KeyPress);
|
||||
break;
|
||||
case 113:
|
||||
setSwitch(PLAYER_1, BUTTON_LEFT, event_return->type == KeyPress);
|
||||
break;
|
||||
case 114:
|
||||
setSwitch(PLAYER_1, BUTTON_RIGHT, event_return->type == KeyPress);
|
||||
break;
|
||||
case 10:
|
||||
setSwitch(PLAYER_1, BUTTON_START, event_return->type == KeyPress);
|
||||
break;
|
||||
case 24:
|
||||
setSwitch(PLAYER_1, BUTTON_1, event_return->type == KeyPress);
|
||||
break;
|
||||
case 25:
|
||||
setSwitch(PLAYER_1, BUTTON_2, event_return->type == KeyPress);
|
||||
break;
|
||||
case 26:
|
||||
setSwitch(PLAYER_1, BUTTON_3, event_return->type == KeyPress);
|
||||
break;
|
||||
case 27:
|
||||
setSwitch(PLAYER_1, BUTTON_4, event_return->type == KeyPress);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MotionNotify:
|
||||
{
|
||||
setAnalogue(ANALOGUE_1, ((double)event_return->xmotion.x / (double)getConfig()->width) * pow(2, 10));
|
||||
setAnalogue(ANALOGUE_2, ((double)event_return->xmotion.y / (double)getConfig()->height) * pow(2, 10));
|
||||
}
|
||||
break;
|
||||
|
||||
case ButtonPress:
|
||||
case ButtonRelease:
|
||||
{
|
||||
switch (event_return->xbutton.button)
|
||||
{
|
||||
case 1: // Trigger
|
||||
setSwitch(PLAYER_1, BUTTON_1, event_return->type == ButtonPress);
|
||||
break;
|
||||
case 3: // Reload
|
||||
setSwitch(PLAYER_1, BUTTON_2, event_return->type == ButtonPress);
|
||||
break;
|
||||
case 9: // Gun Button
|
||||
setSwitch(PLAYER_1, BUTTON_3, event_return->type == ButtonPress);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
1
src/lindbergh/input.h
Normal file
1
src/lindbergh/input.h
Normal file
@ -0,0 +1 @@
|
||||
int initInput();
|
@ -3,6 +3,7 @@
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <pthread.h> /* POSIX threads API to create and manage threads in the program */
|
||||
|
||||
/* The in and out packets used to read and write to and from*/
|
||||
JVSPacket inputPacket, outputPacket;
|
||||
@ -13,6 +14,8 @@ unsigned char outputBuffer[JVS_MAX_PACKET_SIZE], inputBuffer[JVS_MAX_PACKET_SIZE
|
||||
/* Holds the status of the sense line */
|
||||
int senseLine = 3;
|
||||
|
||||
pthread_mutex_t jvsMutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
JVSIO io = {0};
|
||||
|
||||
/**
|
||||
@ -30,9 +33,9 @@ int initJVS()
|
||||
io.capabilities.switches = 14;
|
||||
io.capabilities.coins = 2;
|
||||
io.capabilities.players = 2;
|
||||
io.capabilities.analogueInBits = 8;
|
||||
io.capabilities.analogueInBits = 10;
|
||||
io.capabilities.rightAlignBits = 0;
|
||||
io.capabilities.analogueInChannels = 20;
|
||||
io.capabilities.analogueInChannels = 8;
|
||||
io.capabilities.generalPurposeOutputs = 20;
|
||||
io.capabilities.commandVersion = 19;
|
||||
io.capabilities.jvsVersion = 48;
|
||||
@ -160,7 +163,7 @@ void writeFeatures(JVSPacket *outputPacket, JVSCapabilities *capabilities)
|
||||
*
|
||||
* @returns The status of the entire operation
|
||||
*/
|
||||
JVSStatus processPacket()
|
||||
JVSStatus processPacket(int *packetSize)
|
||||
{
|
||||
readPacket(&inputPacket);
|
||||
|
||||
@ -177,6 +180,8 @@ JVSStatus processPacket()
|
||||
/* Set the entire packet success line */
|
||||
outputPacket.data[outputPacket.length++] = STATUS_SUCCESS;
|
||||
|
||||
pthread_mutex_lock(&jvsMutex);
|
||||
|
||||
while (index < inputPacket.length - 1)
|
||||
{
|
||||
int size = 1;
|
||||
@ -260,6 +265,7 @@ JVSStatus processPacket()
|
||||
outputPacket.data[outputPacket.length] = REPORT_SUCCESS;
|
||||
outputPacket.data[outputPacket.length + 1] = io.state.inputSwitch[0];
|
||||
outputPacket.length += 2;
|
||||
|
||||
for (int i = 0; i < inputPacket.data[index + 1]; i++)
|
||||
{
|
||||
for (int j = 0; j < inputPacket.data[index + 2]; j++)
|
||||
@ -272,7 +278,7 @@ JVSStatus processPacket()
|
||||
|
||||
case CMD_READ_COINS:
|
||||
{
|
||||
////printf("CMD_READ_COINS\n");
|
||||
// printf("CMD_READ_COINS\n");
|
||||
size = 2;
|
||||
int numberCoinSlots = inputPacket.data[index + 1];
|
||||
outputPacket.data[outputPacket.length++] = REPORT_SUCCESS;
|
||||
@ -288,7 +294,7 @@ JVSStatus processPacket()
|
||||
|
||||
case CMD_READ_ANALOGS:
|
||||
{
|
||||
// printf("CMD_READ_ANALOGS\n");
|
||||
// printf("CMD_READ_ANALOGS %d\n", inputPacket.data[index + 1]);
|
||||
size = 2;
|
||||
|
||||
outputPacket.data[outputPacket.length++] = REPORT_SUCCESS;
|
||||
@ -470,7 +476,9 @@ JVSStatus processPacket()
|
||||
index += size;
|
||||
}
|
||||
|
||||
writePacket(&outputPacket);
|
||||
pthread_mutex_unlock(&jvsMutex);
|
||||
|
||||
writePacket(&outputPacket, packetSize);
|
||||
|
||||
return JVS_STATUS_SUCCESS;
|
||||
}
|
||||
@ -557,7 +565,7 @@ JVSStatus readPacket(JVSPacket *packet)
|
||||
*
|
||||
* @param packet The packet to send
|
||||
*/
|
||||
JVSStatus writePacket(JVSPacket *packet)
|
||||
JVSStatus writePacket(JVSPacket *packet, int *packetSize)
|
||||
{
|
||||
/* Get pointer to raw data in packet */
|
||||
unsigned char *packetPointer = (unsigned char *)packet;
|
||||
@ -595,6 +603,9 @@ JVSStatus writePacket(JVSPacket *packet)
|
||||
outputBuffer[outputIndex++] = checksum;
|
||||
}
|
||||
|
||||
// Communicate the output size based on the now escaped bytes
|
||||
*packetSize = outputIndex;
|
||||
|
||||
return JVS_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@ -623,7 +634,6 @@ int setSwitch(JVSPlayer player, JVSInput switchNumber, int value)
|
||||
{
|
||||
io.state.inputSwitch[player] &= ~switchNumber;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -638,7 +648,10 @@ int incrementCoin(JVSPlayer player, int amount)
|
||||
|
||||
int setAnalogue(JVSInput channel, int value)
|
||||
{
|
||||
pthread_mutex_lock(&jvsMutex);
|
||||
io.state.analogueChannel[channel] = value;
|
||||
pthread_mutex_unlock(&jvsMutex);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -223,10 +223,10 @@ typedef enum
|
||||
|
||||
int initJVS();
|
||||
|
||||
JVSStatus processPacket();
|
||||
JVSStatus processPacket(int* packetSize);
|
||||
|
||||
JVSStatus readPacket(JVSPacket *packet);
|
||||
JVSStatus writePacket(JVSPacket *packet);
|
||||
JVSStatus writePacket(JVSPacket *packet, int* packetSize);
|
||||
|
||||
/* The in and out packets used to read and write to and from*/
|
||||
extern JVSPacket inputPacket, outputPacket;
|
||||
|
179
src/lindbergh/lindbergh.c
Normal file
179
src/lindbergh/lindbergh.c
Normal file
@ -0,0 +1,179 @@
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define LD_LIBRARY_PATH "LD_LIBRARY_PATH"
|
||||
#define LD_PRELOAD "LD_PRELOAD"
|
||||
#define PRELOAD_FILE_NAME "lindbergh.so"
|
||||
|
||||
// List of all lindbergh executables known, not including the test executables
|
||||
char *games[] = {"Jennifer", "segaboot", "amiM.elf", "abc", "hod4M.elf", "lgj_final", "vt3", "id4.elf", "id5.elf", "lgjsp_app", "gsevo", "vf5", "apacheM.elf", "END"};
|
||||
|
||||
/**
|
||||
* Tests if the game uses a seperate elf for test mode
|
||||
* and updates the command line to this elf.
|
||||
*
|
||||
* @param name The command path to run
|
||||
*/
|
||||
void testModePath(char *name)
|
||||
{
|
||||
// Check if a different testmode elf is used
|
||||
if (strcmp(name, "./hod4M.elf") == 0)
|
||||
{
|
||||
strcpy(name, "./hod4testM.elf");
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(name, "./Jennifer") == 0)
|
||||
{
|
||||
strcpy(name, "../JenTest/JenTest");
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(name, "./apacheM.elf") == 0)
|
||||
{
|
||||
strcpy(name, "./apacheTestM.elf");
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise add the standard -t to the end
|
||||
strcat(name, " -t");
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure the environment variables are set correctly
|
||||
* to run the game.
|
||||
*/
|
||||
void setEnvironmentVariables()
|
||||
{
|
||||
// Ensure the library path is set correctly
|
||||
char libraryPath[128] = {0};
|
||||
|
||||
const char *currentLibraryPath = getenv(LD_LIBRARY_PATH);
|
||||
if (currentLibraryPath != NULL)
|
||||
{
|
||||
strcat(libraryPath, currentLibraryPath);
|
||||
strcat(libraryPath, ":");
|
||||
}
|
||||
|
||||
strcat(libraryPath, ".:lib");
|
||||
|
||||
setenv(LD_LIBRARY_PATH, libraryPath, 1);
|
||||
|
||||
// Ensure the preload path is set correctly
|
||||
setenv(LD_PRELOAD, PRELOAD_FILE_NAME, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the usage for the loader
|
||||
*/
|
||||
void printUsage(char *argv[])
|
||||
{
|
||||
printf("%s [GAME_PATH] [OPTIONS]\n", argv[0]);
|
||||
printf("Options:\n");
|
||||
printf(" --test | -t Runs the test mode\n");
|
||||
printf(" --gdb Runs with GDB\n");
|
||||
printf(" --help Displays this usage text\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Small utility to automatically detect the game and run it without
|
||||
* having to type a long string in.
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
// Ensure environment variables are set correctly
|
||||
setEnvironmentVariables();
|
||||
|
||||
// Look for the games
|
||||
struct dirent *ent;
|
||||
DIR *dir = opendir(".");
|
||||
|
||||
if (dir == NULL)
|
||||
{
|
||||
printf("Error: Could not list files in current directory.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
char *game = NULL;
|
||||
|
||||
int lindberghSharedObjectFound = 0;
|
||||
|
||||
while ((ent = readdir(dir)) != NULL)
|
||||
{
|
||||
|
||||
if (ent->d_type != DT_REG)
|
||||
continue;
|
||||
|
||||
if (strcmp(ent->d_name, PRELOAD_FILE_NAME) == 0)
|
||||
{
|
||||
lindberghSharedObjectFound = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
while (game == NULL)
|
||||
{
|
||||
if (strcmp(games[index], "END") == 0)
|
||||
break;
|
||||
|
||||
if (strcmp(ent->d_name, games[index]) == 0)
|
||||
{
|
||||
game = games[index];
|
||||
break;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
if (game != NULL && lindberghSharedObjectFound)
|
||||
break;
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
|
||||
if (argc > 1 && strcmp(argv[1], "--help") == 0)
|
||||
{
|
||||
printUsage(argv);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
if (!lindberghSharedObjectFound)
|
||||
{
|
||||
printf("Error: The preload object lindbergh.so was not found in this directory.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (game == NULL)
|
||||
{
|
||||
printf("Error: No lindbergh game found in this directory.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Build up the command to start the game
|
||||
char command[128];
|
||||
strcpy(command, "./");
|
||||
strcat(command, game);
|
||||
|
||||
for (int i = 1; i < argc; i++)
|
||||
{
|
||||
if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--test") == 0)
|
||||
{
|
||||
testModePath(command);
|
||||
}
|
||||
|
||||
if (strcmp(argv[i], "--gdb") == 0)
|
||||
{
|
||||
char temp[128];
|
||||
strcpy(temp, "gdb ");
|
||||
strcat(temp, command);
|
||||
strcpy(command, temp);
|
||||
}
|
||||
}
|
||||
|
||||
printf("$ %s\n", command);
|
||||
|
||||
return system(command);
|
||||
}
|
@ -1,10 +1,15 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "patch.h"
|
||||
#include "config.h"
|
||||
#include "hook.h"
|
||||
#include "securityboard.h"
|
||||
|
||||
extern cpuvendor cpu_vendor;
|
||||
|
||||
static void setVariable(uint32_t address, uint32_t value)
|
||||
{
|
||||
@ -14,14 +19,49 @@ static void setVariable(uint32_t address, uint32_t value)
|
||||
|
||||
void *toModify = (void *)(address - (address % pagesize));
|
||||
|
||||
int prot = mprotect(toModify, pagesize, PROT_WRITE);
|
||||
int prot = mprotect(toModify, pagesize, PROT_EXEC | PROT_WRITE);
|
||||
if (prot != 0)
|
||||
{
|
||||
printf("Error: Cannot unprotect memory region to change variable (%d)\n", prot);
|
||||
return;
|
||||
}
|
||||
*variable = value;
|
||||
}
|
||||
|
||||
static void patchMemory(uint32_t address, char *value)
|
||||
{
|
||||
|
||||
size_t size = strlen((void *)value);
|
||||
// printf("Size=%d\n", size);
|
||||
if (size % 2 != 0)
|
||||
{
|
||||
printf("Patch value should be even.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char buf[size / 2];
|
||||
char tmpchr[3];
|
||||
char *p = value;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
memcpy(tmpchr, p, 2);
|
||||
tmpchr[2] = '\0';
|
||||
buf[i] = (int)strtol(tmpchr, NULL, 16);
|
||||
p += 2;
|
||||
}
|
||||
|
||||
int pagesize = sysconf(_SC_PAGE_SIZE);
|
||||
|
||||
void *toModify = (void *)(address - (address % pagesize));
|
||||
|
||||
int prot = mprotect(toModify, pagesize, PROT_EXEC | PROT_WRITE);
|
||||
if (prot != 0)
|
||||
{
|
||||
printf("Error: Cannot unprotect memory region to change variable (%d)\n", prot);
|
||||
return;
|
||||
}
|
||||
|
||||
*variable = value;
|
||||
memcpy((uint32_t *)address, buf, size / 2);
|
||||
}
|
||||
|
||||
static void detourFunction(uint32_t address, void *function)
|
||||
@ -44,11 +84,21 @@ static void detourFunction(uint32_t address, void *function)
|
||||
cave[4] = (jumpAddress >> (8 * 3)) & 0xFF;
|
||||
cave[3] = (jumpAddress >> (8 * 2)) & 0xFF;
|
||||
cave[2] = (jumpAddress >> (8 * 1)) & 0xFF;
|
||||
cave[1] = (jumpAddress)&0xFF;
|
||||
cave[1] = (jumpAddress) & 0xFF;
|
||||
|
||||
memcpy((void *)address, cave, 5);
|
||||
}
|
||||
|
||||
int stubRetZero()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int stubRetOne()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int amDongleInit()
|
||||
{
|
||||
return 0;
|
||||
@ -64,81 +114,638 @@ int amDongleUpdate()
|
||||
return 0;
|
||||
}
|
||||
|
||||
int amLibInit()
|
||||
{
|
||||
uint32_t *amLibContext = (uint32_t *)0x08dfa2c0; // 0x0809cb00;
|
||||
*amLibContext = 1;
|
||||
uint32_t *amLibInitializad = (uint32_t *)0x08dfa2c4; // 0x0809cb04;
|
||||
uint16_t *amLibPort1 = (uint16_t *)(0x08dfa2c4 + 4); //(0x0809cb04 + 4);
|
||||
uint16_t *amLibPort2 = (uint16_t *)(0x08dfa2c4 + 4); //(0x0809cb04 + 6);
|
||||
uint32_t *bcLibInitialized = (uint32_t *)(0x08dfa2c4 + 8); // 0x0809cb0c;
|
||||
*amLibInitializad = 1;
|
||||
*amLibPort1 = 0xd000;
|
||||
*amLibPort2 = 0x0004;
|
||||
*bcLibInitialized = 0;
|
||||
int res = ((int (*)(void))0x084dedc4)(); // 0x08065d80)();
|
||||
if (res == 1)
|
||||
*bcLibInitialized = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int amDipswInit()
|
||||
{
|
||||
uint32_t *amDipswContext = (uint32_t *)0x08df9cec; // 0x0809c12c;
|
||||
uint32_t *amDipswContext1 = (uint32_t *)(0x08df9cec + 4); //(0x0809c12c + 4);
|
||||
uint32_t *amDipswContext2 = (uint32_t *)(0x08df9cec + 8); //(0x0809c12c + 8);
|
||||
uint32_t *amDipswContext3 = (uint32_t *)(0x08df9cec + 12); //(0x0809c12c + 12);
|
||||
// typedef void *(*___constant_c_and_count_memset)(uint32_t *, int, size_t);
|
||||
//___constant_c_and_count_memset func = (___constant_c_and_count_memset)//0x0805c3d5;
|
||||
// func(amDipswContext, 0, 4);
|
||||
*amDipswContext = 1;
|
||||
*amDipswContext1 = 1;
|
||||
*amDipswContext2 = 1;
|
||||
*amDipswContext3 = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void print_binary(unsigned int number)
|
||||
{
|
||||
if (number >> 1)
|
||||
{
|
||||
print_binary(number >> 1);
|
||||
}
|
||||
putc((number & 1) ? '1' : '0', stdout);
|
||||
}
|
||||
|
||||
int amDipswGetData(uint8_t *dip)
|
||||
{
|
||||
// printf("amDipswGetData Called!!!!!\n");
|
||||
uint8_t result;
|
||||
uint32_t data;
|
||||
|
||||
securityBoardIn(0x38, &data);
|
||||
|
||||
result = (~data & 4) != 0; // Test Button
|
||||
if ((~data & 8) != 0)
|
||||
result |= 2; // Service Button
|
||||
if ((~data & 0x10) != 0)
|
||||
result |= 4; // ??
|
||||
if ((char)data >= 0)
|
||||
result |= 8; // ??
|
||||
if ((~data & 0x100) != 0)
|
||||
result |= 0x10; // Rotation
|
||||
if ((~data & 0x200) != 0)
|
||||
result |= 0x20; // Resolution Dip 4
|
||||
if ((~data & 0x400) != 0)
|
||||
result |= 0x40; // Resolution Dip 5
|
||||
if ((~data & 0x800) != 0)
|
||||
result |= 0x80; // Resolution Dip 6
|
||||
*dip = result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void _putConsole(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
while (*format)
|
||||
{
|
||||
if (*format == '%')
|
||||
{
|
||||
format++;
|
||||
if ((*format == 'd') || (*format == 'n'))
|
||||
{
|
||||
printf("%d", va_arg(args, int));
|
||||
}
|
||||
else if (*format == 's')
|
||||
{
|
||||
printf("%s", va_arg(args, char *));
|
||||
}
|
||||
else if (*format == 'u')
|
||||
{
|
||||
printf("%u", va_arg(args, unsigned int));
|
||||
}
|
||||
else if (*format == '0')
|
||||
{
|
||||
format++;
|
||||
if (*format == '2')
|
||||
{
|
||||
format++;
|
||||
printf("%02X", va_arg(args, int));
|
||||
}
|
||||
else if (*format == '4')
|
||||
{
|
||||
format++;
|
||||
printf("%04X", va_arg(args, unsigned int));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("\nFormat: %c.\n", *format);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
putchar(*format);
|
||||
}
|
||||
format++;
|
||||
}
|
||||
va_end(args);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int initPatch()
|
||||
{
|
||||
Game game = getConfig()->game;
|
||||
EmulatorConfig *config = getConfig();
|
||||
|
||||
switch (game)
|
||||
switch (config->game)
|
||||
{
|
||||
case RTUNED:
|
||||
case R_TUNED:
|
||||
{
|
||||
// Security
|
||||
detourFunction(0x08366846, amDongleInit);
|
||||
detourFunction(0x08365301, amDongleIsAvailable);
|
||||
detourFunction(0x08365cf7, amDongleUpdate);
|
||||
}
|
||||
break;
|
||||
|
||||
case SRTV:
|
||||
case SEGA_RACE_TV:
|
||||
{
|
||||
// Security
|
||||
detourFunction(0x084d5b40, amDongleInit);
|
||||
detourFunction(0x084d45f9, amDongleIsAvailable);
|
||||
detourFunction(0x084d4fef, amDongleUpdate);
|
||||
// Fixes
|
||||
detourFunction(0x084d44fc, stubRetZero); // Stub amDipswSetLed
|
||||
detourFunction(0x084d4485, amDipswGetData);
|
||||
}
|
||||
break;
|
||||
|
||||
case ABC:
|
||||
case AFTER_BURNER_CLIMAX_REVA:
|
||||
{
|
||||
if (config->showDebugMessages == 1)
|
||||
{
|
||||
// Debug Messages
|
||||
setVariable(0x0a0a37e4, 2); // amBackupDebugLevel
|
||||
setVariable(0x0a0a3800, 2); // amCreditDebugLevel
|
||||
setVariable(0x0a0a3a58, 2); // amDipswDebugLevel
|
||||
setVariable(0x0a0a3a5c, 2); // amDongleDebugLevel
|
||||
setVariable(0x0a0a3a60, 2); // amEepromDebugLevel
|
||||
setVariable(0x0a0a3a64, 2); // amHwmonitorDebugLevel
|
||||
setVariable(0x0a0a3a68, 2); // amJvsDebugLevel
|
||||
setVariable(0x0a0a3a6c, 2); // amLibDebugLevel
|
||||
setVariable(0x0a0a3a70, 2); // amMiscDebugLevel
|
||||
setVariable(0x0a0a3a74, 2); // amOsinfoDebugLevel
|
||||
setVariable(0x0a0a3a78, 2); // amSysDataDebugLevel
|
||||
setVariable(0x0a0a3a80, 2); // bcLibDebugLevel
|
||||
}
|
||||
// Security
|
||||
detourFunction(0x081e4980, amDongleInit);
|
||||
detourFunction(0x081e4cce, amDongleIsAvailable);
|
||||
detourFunction(0x081e4bfa, amDongleUpdate);
|
||||
// Fixes
|
||||
detourFunction(0x081e48b6, amDipswGetData);
|
||||
detourFunction(0x081e492e, stubRetZero); // Stub amDipswSetLed
|
||||
// Does not work
|
||||
// setVariable(0x08061c31, 0x0000000c); // Force HD resolution
|
||||
}
|
||||
break;
|
||||
case AFTER_BURNER_CLIMAX_REVB:
|
||||
{
|
||||
if (config->showDebugMessages == 1)
|
||||
{
|
||||
// Debug Messages
|
||||
setVariable(0x0a0a0d24, 2); // amBackupDebugLevel
|
||||
setVariable(0x0a0a0d40, 2); // amCreditDebugLevel
|
||||
setVariable(0x0a0a0f98, 2); // amDipswDebugLevel
|
||||
setVariable(0x0a0a0f9c, 2); // amDongleDebugLevel
|
||||
setVariable(0x0a0a0fa0, 2); // amEepromDebugLevel
|
||||
setVariable(0x0a0a0fa4, 2); // amHwmonitorDebugLevel
|
||||
setVariable(0x0a0a0fa8, 2); // amJvsDebugLevel
|
||||
setVariable(0x0a0a0fac, 2); // amLibDebugLevel
|
||||
setVariable(0x0a0a0fb0, 2); // amMiscDebugLevel
|
||||
setVariable(0x0a0a0fb4, 2); // amOsinfoDebugLevel
|
||||
setVariable(0x0a0a0fb8, 2); // amSysDataDebugLevel
|
||||
setVariable(0x0a0a0fc0, 2); // bcLibDebugLevel
|
||||
}
|
||||
// Security
|
||||
detourFunction(0x081e3424, amDongleInit);
|
||||
detourFunction(0x081e3772, amDongleIsAvailable);
|
||||
detourFunction(0x081e369e, amDongleUpdate);
|
||||
// Fixes
|
||||
detourFunction(0x081e335a, amDipswGetData);
|
||||
detourFunction(0x081e33d2, stubRetZero); // Stub amDipswSetLed
|
||||
}
|
||||
break;
|
||||
case OUTRUN:
|
||||
case OUTRUN_2_SP_SDX_REVA:
|
||||
{
|
||||
setVariable(0x0893a24c, 2); // amBackupDebugLevel
|
||||
setVariable(0x0893a260, 2); // amCreditDebugLevel
|
||||
setVariable(0x0893a4b8, 2); // amDipswDebugLevel
|
||||
setVariable(0x0893a4bc, 2); // amDongleDebugLevel
|
||||
setVariable(0x0893a4c0, 2); // amEepromDebugLevel
|
||||
setVariable(0x0893a4c4, 2); // amHwmonitorDebugLevel
|
||||
setVariable(0x0893a4c8, 2); // amJvsDebugLevel
|
||||
setVariable(0x0893a4cc, 2); // amLibDebugLevel
|
||||
setVariable(0x0893a4d0, 2); // amMiscDebugLevel
|
||||
setVariable(0x0893a4d4, 2); // amOsinfoDebugLevel
|
||||
setVariable(0x0893a4d8, 2); // amSysDataDebugLevel
|
||||
setVariable(0x0893a4e0, 2); // bcLibDebugLevel
|
||||
if (config->showDebugMessages == 1)
|
||||
{
|
||||
// Debug Messages
|
||||
setVariable(0x0893a24c, 2); // amBackupDebugLevel
|
||||
setVariable(0x0893a260, 2); // amCreditDebugLevel
|
||||
setVariable(0x0893a4b8, 2); // amDipswDebugLevel
|
||||
setVariable(0x0893a4bc, 2); // amDongleDebugLevel
|
||||
setVariable(0x0893a4c0, 2); // amEepromDebugLevel
|
||||
setVariable(0x0893a4c4, 2); // amHwmonitorDebugLevel
|
||||
setVariable(0x0893a4c8, 2); // amJvsDebugLevel
|
||||
setVariable(0x0893a4cc, 2); // amLibDebugLevel
|
||||
setVariable(0x0893a4d0, 2); // amMiscDebugLevel
|
||||
setVariable(0x0893a4d4, 2); // amOsinfoDebugLevel
|
||||
setVariable(0x0893a4d8, 2); // amSysDataDebugLevel
|
||||
setVariable(0x0893a4e0, 2); // bcLibDebugLevel
|
||||
}
|
||||
// Security
|
||||
detourFunction(0x08190e80, amDongleInit);
|
||||
detourFunction(0x08191201, amDongleIsAvailable);
|
||||
detourFunction(0x08191125, amDongleUpdate);
|
||||
// Fixes
|
||||
detourFunction(0x08190db6, amDipswGetData);
|
||||
detourFunction(0x08190e2e, stubRetZero); // Stub amDipswSetLed
|
||||
}
|
||||
break;
|
||||
|
||||
case THE_HOUSE_OF_THE_DEAD_4:
|
||||
{
|
||||
setVariable(0x0a737c60, 2); // amBackupDebugLevel
|
||||
setVariable(0x0a737c64, 2); // amChunkDataDebugLevel
|
||||
setVariable(0x0a737c80, 2); // amCreditDebugLevel
|
||||
setVariable(0x0a737ed8, 2); // amDipswDebugLevel
|
||||
setVariable(0x0a737edc, 2); // amDiskDebugLevel
|
||||
setVariable(0x0a737ee0, 2); // amDongleDebugLevel
|
||||
setVariable(0x0a737ee4, 2); // amEepromDebugLevel
|
||||
setVariable(0x0a737ee8, 2); // amHmDebugLevel
|
||||
setVariable(0x0a737ef0, 2); // amJvsDebugLevel
|
||||
setVariable(0x0a737f14, 2); // amLibDebugLevel
|
||||
setVariable(0x0a737f18, 2); // amMiscDebugLevel
|
||||
setVariable(0x0a737f1c, 2); // amSysDataDebugLevel
|
||||
setVariable(0x0a737f20, 2); // bcLibDebugLevel
|
||||
setVariable(0x0a737f24, 0x0FFFFFFF); // s_logMask
|
||||
if (config->showDebugMessages == 1)
|
||||
{
|
||||
// Debug Messages
|
||||
setVariable(0x0a737c60, 2); // amBackupDebugLevel
|
||||
setVariable(0x0a737c64, 2); // amChunkDataDebugLevel
|
||||
setVariable(0x0a737c80, 2); // amCreditDebugLevel
|
||||
setVariable(0x0a737ed8, 2); // amDipswDebugLevel
|
||||
setVariable(0x0a737edc, 2); // amDiskDebugLevel
|
||||
setVariable(0x0a737ee0, 2); // amDongleDebugLevel
|
||||
setVariable(0x0a737ee4, 2); // amEepromDebugLevel
|
||||
setVariable(0x0a737ee8, 2); // amHmDebugLevel
|
||||
setVariable(0x0a737ef0, 2); // amJvsDebugLevel
|
||||
setVariable(0x0a737f14, 2); // amLibDebugLevel
|
||||
setVariable(0x0a737f18, 2); // amMiscDebugLevel
|
||||
setVariable(0x0a737f1c, 2); // amSysDataDebugLevel
|
||||
setVariable(0x0a737f20, 2); // bcLibDebugLevel
|
||||
setVariable(0x0a737f24, 0x0FFFFFFF); // s_logMask
|
||||
}
|
||||
// Security
|
||||
detourFunction(0x08320178, amDongleInit);
|
||||
detourFunction(0x08320459, amDongleIsAvailable);
|
||||
detourFunction(0x083203c0, amDongleUpdate);
|
||||
// Fixes
|
||||
detourFunction(0x0831ddd7, amDipswGetData);
|
||||
detourFunction(0x0831de4f, stubRetZero); // Stub amDipswSetLed
|
||||
// CPU patch to support AMD processors
|
||||
if (strcmp("AuthenticAMD", cpu_vendor.cpuid) == 0)
|
||||
{
|
||||
setVariable(0x0837d6aa, cpu_vendor.ebx);
|
||||
setVariable(0x0837d6ba, cpu_vendor.edx);
|
||||
setVariable(0x0837d6c5, cpu_vendor.ecx);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case THE_HOUSE_OF_THE_DEAD_4_STRIPPED:
|
||||
{
|
||||
// Security
|
||||
detourFunction(0x0831ad04, amDongleInit);
|
||||
detourFunction(0x0831b017, amDongleIsAvailable);
|
||||
detourFunction(0x0831af7e, amDongleUpdate);
|
||||
// Fixes
|
||||
detourFunction(0x0831875f, amDipswGetData);
|
||||
detourFunction(0x083187d7, stubRetZero); // Stub amDipswSetLed
|
||||
// CPU patch to support AMD processors
|
||||
if (strcmp("AuthenticAMD", cpu_vendor.cpuid) == 0)
|
||||
{
|
||||
setVariable(0x0837963a, cpu_vendor.ebx);
|
||||
setVariable(0x0837964a, cpu_vendor.edx);
|
||||
setVariable(0x08379655, cpu_vendor.ecx);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case THE_HOUSE_OF_THE_DEAD_4_TEST:
|
||||
{
|
||||
detourFunction(0x080677a0, amDongleInit);
|
||||
detourFunction(0x08067a81, amDongleIsAvailable);
|
||||
detourFunction(0x080679e8, amDongleUpdate);
|
||||
// Fixes
|
||||
detourFunction(0x08067653, amDipswGetData);
|
||||
detourFunction(0x080676cb, stubRetZero); // Stub amDipswSetLed
|
||||
// CPU patch to support AMD processors
|
||||
if (strcmp("AuthenticAMD", cpu_vendor.cpuid) == 0)
|
||||
{
|
||||
setVariable(0x0807217a, cpu_vendor.ebx);
|
||||
setVariable(0x0807218a, cpu_vendor.edx);
|
||||
setVariable(0x08072195, cpu_vendor.ecx);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case THE_HOUSE_OF_THE_DEAD_4_SPECIAL:
|
||||
{
|
||||
detourFunction(0x08363438, amDongleInit);
|
||||
detourFunction(0x0836374b, amDongleIsAvailable);
|
||||
detourFunction(0x083636b2, amDongleUpdate);
|
||||
patchMemory(0x081f9491, "9090");
|
||||
patchMemory(0x081f9499, "01");
|
||||
// Fixes
|
||||
detourFunction(0x08360e93, amDipswGetData);
|
||||
detourFunction(0x08360f0b, stubRetZero); // Stub amDipswSetLed
|
||||
// CPU patch to support AMD processors
|
||||
if (strcmp("AuthenticAMD", cpu_vendor.cpuid) == 0)
|
||||
{
|
||||
setVariable(0x083cef0a, cpu_vendor.ebx);
|
||||
setVariable(0x083cef1a, cpu_vendor.edx);
|
||||
setVariable(0x083cef25, cpu_vendor.ecx);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case THE_HOUSE_OF_THE_DEAD_4_SPECIAL_TEST:
|
||||
{
|
||||
detourFunction(0x0806e914, amDongleInit);
|
||||
detourFunction(0x0806ec27, amDongleIsAvailable);
|
||||
detourFunction(0x0806eb8e, amDongleUpdate);
|
||||
// Fixes
|
||||
detourFunction(0x0806e7c7, amDipswGetData);
|
||||
detourFunction(0x0806e83f, stubRetZero); // Stub amDipswSetLed
|
||||
// CPU patch to support AMD processors
|
||||
if (strcmp("AuthenticAMD", cpu_vendor.cpuid) == 0)
|
||||
{
|
||||
setVariable(0x0807a3ba, cpu_vendor.ebx);
|
||||
setVariable(0x0807a3ca, cpu_vendor.edx);
|
||||
setVariable(0x0807a3d5, cpu_vendor.ecx);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case THE_HOUSE_OF_THE_DEAD_EX:
|
||||
{
|
||||
detourFunction(0x084ba886, amDongleInit);
|
||||
detourFunction(0x084b9341, amDongleIsAvailable);
|
||||
detourFunction(0x084b9d37, amDongleUpdate);
|
||||
// Fixes
|
||||
detourFunction(0x084b6a69, amDipswGetData);
|
||||
detourFunction(0x084b6adf, stubRetZero); // Stub amDipswSetLed
|
||||
// CPU patch to support AMD processors
|
||||
if (strcmp("AuthenticAMD", cpu_vendor.cpuid) == 0)
|
||||
{
|
||||
setVariable(0x0849E2AD, cpu_vendor.ebx);
|
||||
setVariable(0x0849E2B7, cpu_vendor.edx);
|
||||
setVariable(0x0849E2C1, cpu_vendor.ecx);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case THE_HOUSE_OF_THE_DEAD_EX_TEST:
|
||||
{
|
||||
detourFunction(0x08078996, amDongleInit);
|
||||
detourFunction(0x08077451, amDongleIsAvailable);
|
||||
detourFunction(0x08077e47, amDongleUpdate);
|
||||
// Fixes
|
||||
detourFunction(0x080772dd, amDipswGetData);
|
||||
detourFunction(0x08077353, stubRetZero); // Stub amDipswSetLed
|
||||
// CPU patch to support AMD processors
|
||||
if (strcmp("AuthenticAMD", cpu_vendor.cpuid) == 0)
|
||||
{
|
||||
setVariable(0x080847BD, cpu_vendor.ebx);
|
||||
setVariable(0x080847C7, cpu_vendor.edx);
|
||||
setVariable(0x080847D1, cpu_vendor.ecx);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case VIRTUA_FIGHTER_5_REVC:
|
||||
{
|
||||
// Security
|
||||
detourFunction(0x085c6010, amDongleInit);
|
||||
detourFunction(0x085c63cc, amDongleIsAvailable);
|
||||
detourFunction(0x085c62f0, amDongleUpdate);
|
||||
// Fixes and patches to bypss network check
|
||||
detourFunction(0x085c5f46, amDipswGetData);
|
||||
detourFunction(0x085c5fbe, stubRetZero); // Stub amDipswSetLed
|
||||
detourFunction(0x080b3426, stubRetZero); // Stub returns 0
|
||||
detourFunction(0x080cb6d4, stubRetZero); // Stub returns 0
|
||||
detourFunction(0x0840889e, stubRetZero); // Stub returns 0
|
||||
detourFunction(0x0840ab90, stubRetZero); // Stub returns 0
|
||||
patchMemory(0x080e17af, "b800000000"); // Patch IDK what
|
||||
}
|
||||
break;
|
||||
|
||||
case LETS_GO_JUNGLE:
|
||||
{
|
||||
if (config->showDebugMessages == 1)
|
||||
{
|
||||
setVariable(0x08c083a4, 2); // amBackupDebugLevel
|
||||
setVariable(0x08c083c0, 2); // amCreditDebugLevel
|
||||
setVariable(0x08c08618, 2); // amDipswDebugLevel
|
||||
setVariable(0x08c0861c, 2); // amDongleDebugLevel
|
||||
setVariable(0x08c08620, 2); // amEepromDebugLevel
|
||||
setVariable(0x08c08624, 2); // amHwmonitorDebugLevel
|
||||
setVariable(0x08c08628, 2); // amJvsDebugLevel
|
||||
setVariable(0x08c0862c, 2); // amLibDebugLevel
|
||||
setVariable(0x08c08630, 2); // amMiscDebugLevel
|
||||
setVariable(0x08c08638, 2); // amSysDataDebugLevel
|
||||
setVariable(0x08c08640, 2); // bcLibDebugLevel
|
||||
setVariable(0x08c08634, 2); // amOsinfoDebugLevel
|
||||
setVariable(0x08c08644, 0x0FFFFFFF); // s_logMask
|
||||
detourFunction(0x08074a8c, _putConsole); // Debug Messages
|
||||
}
|
||||
// Security
|
||||
detourFunction(0x084e50d8, amDongleInit);
|
||||
detourFunction(0x084e5459, amDongleIsAvailable);
|
||||
detourFunction(0x084e537d, amDongleUpdate);
|
||||
patchMemory(0x0807b76a, "9090"); // Patch initializeArcadeBackup
|
||||
// Fixes
|
||||
detourFunction(0x084e500e, amDipswGetData);
|
||||
detourFunction(0x084e5086, stubRetZero); // Stub amDipswSetLed
|
||||
patchMemory(0x0840d858, "9090"); // No more Full Screen from the Game
|
||||
// Set Resolution
|
||||
// setVariable(0x082E006b, 0x00000780); // Set ResX
|
||||
// setVariable(0x082E0078, 0x00000438); // Set ResY
|
||||
|
||||
// From Teknoparrot AMDFIX
|
||||
// setVariable(0x083ef701, 0x00036ee9); // AMDFIX
|
||||
// setVariable(0x084032e0, 0x8b90c933); // fix shader compilation with AMD GPUs
|
||||
// setVariable(0x08523950, 0x000000c3); // Remove ADXM_SetupFramework (Not necessary)
|
||||
}
|
||||
break;
|
||||
|
||||
case LETS_GO_JUNGLE_SPECIAL:
|
||||
{
|
||||
if (config->showDebugMessages == 1)
|
||||
{
|
||||
setVariable(0x08c453e4, 2); // amBackupDebugLevel
|
||||
setVariable(0x08c45400, 2); // amCreditDebugLevel
|
||||
setVariable(0x08c45658, 2); // amDipswDebugLevel
|
||||
setVariable(0x08c4565c, 2); // amDongleDebugLevel
|
||||
setVariable(0x08c45660, 2); // amEepromDebugLevel
|
||||
setVariable(0x08c45664, 2); // amHwmonitorDebugLevel
|
||||
setVariable(0x08c45668, 2); // amJvsDebugLevel
|
||||
setVariable(0x08c4566c, 2); // amLibDebugLevel
|
||||
setVariable(0x08c45670, 2); // amMiscDebugLevel
|
||||
setVariable(0x08c45678, 2); // amSysDataDebugLevel
|
||||
setVariable(0x08c45680, 2); // bcLibDebugLevel
|
||||
setVariable(0x08c45674, 2); // amOsinfoDebugLevel
|
||||
setVariable(0x08c45684, 0x0FFFFFFF); // s_logMask
|
||||
detourFunction(0x08075012, _putConsole);
|
||||
}
|
||||
// Security
|
||||
detourFunction(0x08510320, amDongleInit);
|
||||
detourFunction(0x085106dc, amDongleIsAvailable);
|
||||
detourFunction(0x08510600, amDongleUpdate);
|
||||
patchMemory(0x0807e609, "909090909090");
|
||||
// Fixes
|
||||
detourFunction(0x08510256, amDipswGetData);
|
||||
detourFunction(0x085102ce, stubRetZero); // Stub amDipswSetLed
|
||||
patchMemory(0x08438954, "9090"); // No more Full Screen from the Game
|
||||
// Set Resolution
|
||||
// setVariable(0x08303C4B, 0x00000780); // Set ResX
|
||||
// setVariable(0x08303C58, 0x00000438); // Set ResY
|
||||
}
|
||||
break;
|
||||
|
||||
case INITIALD_4:
|
||||
{
|
||||
if (config->showDebugMessages == 1)
|
||||
{
|
||||
setVariable(0x08d71750, 2); // amBackupDebugLevel
|
||||
setVariable(0x08d71760, 2); // amCreditDebugLevel
|
||||
setVariable(0x08d719b8, 2); // amDipswDebugLevel
|
||||
setVariable(0x08d719bc, 2); // amDongleDebugLevel
|
||||
setVariable(0x08d719c0, 2); // amEepromDebugLevel
|
||||
setVariable(0x08d719c4, 2); // amHwmonitorDebugLevel
|
||||
setVariable(0x08d719c8, 2); // amJvsDebugLevel
|
||||
setVariable(0x08d719cc, 2); // amLibDebugLevel
|
||||
setVariable(0x08d719d0, 2); // amMiscDebugLevel
|
||||
setVariable(0x08d719d8, 2); // amSysDataDebugLevel
|
||||
setVariable(0x08d719e0, 2); // bcLibDebugLevel
|
||||
setVariable(0x08d719d4, 2); // amOsinfoDebugLevel
|
||||
setVariable(0x08d719e4, 0x0FFFFFFF); // s_logMask
|
||||
detourFunction(0x0808f9a8, _putConsole); // Crashes the game sometimes.
|
||||
}
|
||||
// Security
|
||||
detourFunction(0x086e2336, amDongleInit);
|
||||
detourFunction(0x086e0d81, amDongleIsAvailable);
|
||||
detourFunction(0x086e17e5, amDongleUpdate);
|
||||
// Fixes
|
||||
detourFunction(0x086e0c0d, amDipswGetData);
|
||||
detourFunction(0x086e0c84, stubRetZero); // amDipswSetLED
|
||||
detourFunction(0x0821e6dc, stubRetOne); // isEthLinkUp
|
||||
patchMemory(0x082cb412, "c0270900"); // tickInitStoreNetwork
|
||||
patchMemory(0x082cb6d9, "e950010000"); // tickWaitDHCP
|
||||
// Set Resolution
|
||||
// patchMemory(0x0835664d, "e9f000"); // Force resolution set
|
||||
// setVariable(0x08356743, 0x00000780); // Set ResX
|
||||
// setVariable(0x08356748, 0x00000438); // Set ResY
|
||||
|
||||
// FrameBuffer Resolution (No effect that I know)
|
||||
/*
|
||||
setVariable(0x08248037, 0x00000780); // Set ResX
|
||||
setVariable(0x0824802f, 0x00000438); // Set ResY
|
||||
setVariable(0x082480f7, 0x00000780); // Set ResX
|
||||
setVariable(0x082480ef, 0x00000438); // Set ResY
|
||||
setVariable(0x082481b7, 0x00000780); // Set ResX
|
||||
setVariable(0x082481af, 0x00000438); // Set ResY
|
||||
setVariable(0x08248216, 0x00000780); // Set ResX
|
||||
setVariable(0x0824820e, 0x00000438); // Set ResY
|
||||
|
||||
setVariable(0x082489a7, 0x00000780); // Set ResX
|
||||
setVariable(0x0824899f, 0x00000438); // Set ResY
|
||||
setVariable(0x08248a32, 0x00000780); // Set ResX
|
||||
setVariable(0x08248a2a, 0x00000438); // Set ResY
|
||||
*/
|
||||
|
||||
// IDK if the following work (taken from TP)
|
||||
// setVariable(0x08548ef3, 0x8990c031); // Shader Compiler
|
||||
// setVariable(0x08799d8c, 0x082c9f52); // childTerminationHanlder
|
||||
}
|
||||
break;
|
||||
case INITIALD_4_REVE:
|
||||
{
|
||||
if (config->showDebugMessages == 1)
|
||||
{
|
||||
// Debug
|
||||
detourFunction(0x08090478, _putConsole); // Crashes the game sometimes.
|
||||
}
|
||||
// Security
|
||||
detourFunction(0x087106e6, amDongleInit);
|
||||
detourFunction(0x0870f131, amDongleIsAvailable);
|
||||
detourFunction(0x0870fb95, amDongleUpdate);
|
||||
// Fixes
|
||||
detourFunction(0x0870efbd, amDipswGetData);
|
||||
detourFunction(0x0870f034, stubRetZero); // amDipswSetLed
|
||||
detourFunction(0x08230fde, stubRetOne); // isEthLinkUp
|
||||
patchMemory(0x082df87d, "e954010000"); // tickWaitDHCP
|
||||
patchMemory(0x082e0ec9, "eb60"); // tickInitAddress
|
||||
// setVariable(0x08580979, 0x000126e9); // Avoid Full Screen set from Game
|
||||
// Set Resolution
|
||||
// setVariable(0x0837b12d, 0x0000f0e9); // Force set resolution
|
||||
// setVariable(0x0837b223, 0x00000550); // Set ResX
|
||||
// setVariable(0x0837b228, 0x00000300); // Set ResY
|
||||
// setVariable(0x085700d3, 0x8990c031); // Fix something with the Shaders??
|
||||
}
|
||||
break;
|
||||
case SEGABOOT_2_4_SYM:
|
||||
{
|
||||
if (config->showDebugMessages == 1)
|
||||
{
|
||||
setVariable(0x0808da48, 2); // amAdtecDebugLevel
|
||||
setVariable(0x0808cf8c, 2); // amAtaDebugLevel
|
||||
setVariable(0x0808cf90, 2); // amBackupDebugLevel
|
||||
setVariable(0x0808cf94, 2); // amChunkDataDebugLevel
|
||||
setVariable(0x0808cfa0, 2); // amCreditDebugLevel
|
||||
setVariable(0x0808d1f8, 2); // amDipswDebugLevel
|
||||
setVariable(0x0808d1fc, 2); // amDiskDebugLevel
|
||||
setVariable(0x0808d200, 2); // amDongleDebugLevel
|
||||
setVariable(0x0808d204, 2); // amEepromDebugLevel
|
||||
setVariable(0x0808d208, 2); // amHmDebugLevel
|
||||
setVariable(0x0808d210, 2); // amJvsDebugLevel
|
||||
setVariable(0x0808d214, 2); // amLibDebugLevel
|
||||
setVariable(0x0808d218, 2); // amMiscDebugLevel
|
||||
setVariable(0x0808d21c, 2); // amSysDataDebugLevel
|
||||
setVariable(0x0808d220, 2); // bcLibDebugLevel
|
||||
setVariable(0x0808cf58, 2); // g_DebugLevel
|
||||
setVariable(0x0808d224, 0x0FFFFFFF); // logmask
|
||||
}
|
||||
|
||||
detourFunction(0x0805e8b0, amDongleInit);
|
||||
detourFunction(0x0805ebc3, amDongleIsAvailable);
|
||||
detourFunction(0x0805eb2a, amDongleUpdate);
|
||||
detourFunction(0x0805c30b, amDipswGetData);
|
||||
}
|
||||
break;
|
||||
case VIRTUA_TENNIS_3:
|
||||
{
|
||||
// Security
|
||||
detourFunction(0x0831c724, amDongleInit);
|
||||
detourFunction(0x0831ca37, amDongleIsAvailable);
|
||||
detourFunction(0x0831c99e, amDongleUpdate);
|
||||
// Fixes
|
||||
detourFunction(0x0831c5d7, amDipswGetData);
|
||||
detourFunction(0x0831c64f, stubRetZero);
|
||||
setVariable(0x0827ae1b, 0x34891beb); // Disable Fullscreen set from the game
|
||||
}
|
||||
break;
|
||||
case VIRTUA_TENNIS_3_TEST:
|
||||
{
|
||||
if (config->showDebugMessages == 1)
|
||||
{
|
||||
// Debug
|
||||
detourFunction(0x08054d14, _putConsole); // Crashes the game sometimes.
|
||||
}
|
||||
// Security
|
||||
detourFunction(0x0815f610, amDongleInit);
|
||||
detourFunction(0x0815f923, amDongleIsAvailable);
|
||||
detourFunction(0x0815f88a, amDongleUpdate);
|
||||
// Fixes
|
||||
detourFunction(0x0815d06b, amDipswGetData);
|
||||
detourFunction(0x0815d0e3, stubRetZero);
|
||||
}
|
||||
break;
|
||||
case RAMBO:
|
||||
{
|
||||
// Security
|
||||
detourFunction(0x082c4746, amDongleInit);
|
||||
detourFunction(0x082c3201, amDongleIsAvailable);
|
||||
detourFunction(0x082c3bf7, amDongleUpdate);
|
||||
// Fixes
|
||||
detourFunction(0x082c308d, amDipswGetData);
|
||||
detourFunction(0x082c3103, stubRetZero);
|
||||
}
|
||||
break;
|
||||
case TOO_SPICY:
|
||||
{
|
||||
// Security
|
||||
detourFunction(0x0831cf02, amDongleInit);
|
||||
detourFunction(0x0831b94d, amDongleIsAvailable);
|
||||
detourFunction(0x0831c3b1, amDongleUpdate);
|
||||
// Fixes
|
||||
detourFunction(0x0831907d, amDipswGetData);
|
||||
detourFunction(0x083190f4, stubRetZero);
|
||||
// CPU patch to support AMD processors
|
||||
if (strcmp("AuthenticAMD", cpu_vendor.cpuid) == 0)
|
||||
{
|
||||
setVariable(0x08399ADA, cpu_vendor.ebx);
|
||||
setVariable(0x08399AEA, cpu_vendor.edx);
|
||||
setVariable(0x08399AF5, cpu_vendor.ecx);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Don't do any patches for random games
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
28
src/lindbergh/pcidata.h
Normal file
28
src/lindbergh/pcidata.h
Normal file
@ -0,0 +1,28 @@
|
||||
const char pcidata[256] = {
|
||||
0x86, 0x80, 0x18, 0x29, 0x03, 0x01, 0x00, 0x00, 0x02, 0x00, 0x01,
|
||||
0x06, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xf4, 0x1a, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06,
|
||||
0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x8a, 0x8b,
|
||||
0x8b, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x8a, 0x8b, 0x8b, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0,
|
||||
0xd1, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
const int pcidata_length = 256;
|
@ -106,26 +106,26 @@ int securityBoardIn(uint16_t port, uint32_t *data)
|
||||
case SECURITY_BOARD_FRONT_PANEL:
|
||||
{
|
||||
uint32_t result = 0xFFFFFFFF;
|
||||
if (securityBoard.serviceSwitch)
|
||||
result &= ~0x08;
|
||||
if (securityBoard.testSwitch)
|
||||
result &= ~0x04;
|
||||
if (securityBoard.dipSwitch[6])
|
||||
if (securityBoard.dipSwitch[6]) // bit12
|
||||
result &= ~0x800; // DIP 6
|
||||
if (securityBoard.dipSwitch[5])
|
||||
if (securityBoard.dipSwitch[5]) // bit11
|
||||
result &= ~0x400; // DIP 5
|
||||
if (securityBoard.dipSwitch[4])
|
||||
if (securityBoard.dipSwitch[4]) // bit10
|
||||
result &= ~0x200; // DIP 4
|
||||
if (securityBoard.dipSwitch[3])
|
||||
if (securityBoard.dipSwitch[3]) // bit9
|
||||
result &= ~0x100; // DIP 3
|
||||
if (securityBoard.dipSwitch[2])
|
||||
if (securityBoard.dipSwitch[2]) // bit8
|
||||
result &= ~0x80; // DIP 2
|
||||
if (securityBoard.dipSwitch[1])
|
||||
if (securityBoard.dipSwitch[1]) // bit7
|
||||
result &= ~0x40; // DIP 1
|
||||
if (securityBoard.dipSwitch[8])
|
||||
if (securityBoard.dipSwitch[8]) // bit6
|
||||
result &= ~0x20;
|
||||
if (securityBoard.dipSwitch[7])
|
||||
if (securityBoard.dipSwitch[7]) // bit5
|
||||
result &= ~0x10;
|
||||
if (securityBoard.serviceSwitch) // bit4
|
||||
result &= ~0x08;
|
||||
if (securityBoard.testSwitch) // bit3
|
||||
result &= ~0x04;
|
||||
*data = result;
|
||||
}
|
||||
break;
|
||||
|
Loading…
Reference in New Issue
Block a user