Add retrolaunch

Signed-off-by: Saggi Mizrahi <ficoos@gmail.com>
This commit is contained in:
Saggi Mizrahi 2013-01-10 23:49:12 -05:00
parent 7411d140f8
commit 15ad74fdad
12 changed files with 1543 additions and 1 deletions

View File

@ -1,6 +1,6 @@
include config.mk
TARGET = retroarch tools/retroarch-joyconfig
TARGET = retroarch tools/retroarch-joyconfig tools/retrolaunch/retrolaunch
OBJ = retroarch.o \
file.o \
@ -35,6 +35,17 @@ JOYCONFIG_OBJ = tools/retroarch-joyconfig.o \
compat/compat.o \
input/input_common.o
RETROLAUNCH_OBJ = tools/retrolaunch/main.o \
tools/retrolaunch/sha1.o \
tools/retrolaunch/parser.o \
tools/retrolaunch/cd_detect.o \
tools/retrolaunch/rl_fnmatch.o \
file_path.o \
compat/compat.o \
conf/config_file.o \
settings.o \
$(NULL)
HEADERS = $(wildcard */*.h) $(wildcard *.h)
ifeq ($(findstring Haiku,$(OS)),)
@ -339,6 +350,10 @@ else
$(Q)$(CC) -o $@ $(JOYCONFIG_OBJ) $(SDL_LIBS) $(LDFLAGS) $(LIBRARY_DIRS)
endif
tools/retrolaunch/retrolaunch: $(RETROLAUNCH_OBJ)
@$(if $(Q), $(shell echo echo LD $@),)
$(Q)$(LD) -o $@ $(RETROLAUNCH_OBJ) $(LIBS) $(LDFLAGS) $(LIBRARY_DIRS)
%.o: %.c config.h config.mk $(HEADERS)
@$(if $(Q), $(shell echo echo CC $<),)
$(Q)$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $<

View File

@ -0,0 +1,314 @@
#include "parser.h"
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <limits.h>
#include <stdio.h>
#include <libgen.h>
#include <stdlib.h>
#include "log.h"
#define MAGIC_LEN 16
struct MagicEntry {
char* system_name;
char* magic;
};
static struct MagicEntry MAGIC_NUMBERS[] = {
{"ps1", "\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x02\x00\x02\x00"},
{"pcecd", "\x82\xb1\x82\xcc\x83\x76\x83\x8d\x83\x4f\x83\x89\x83\x80\x82\xcc\x92"},
{"scd", "\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x02\x00\x01\x53"},
{NULL, NULL}
};
static int find_first_data_track(const char* cue_path, off_t* offset,
char* track_path, size_t max_len) {
int rv;
int fd = -1;
char tmp_token[MAX_TOKEN_LEN];
int m, s, f;
char* cue_path_copy;
char* cue_dir;
cue_path_copy = strdup(cue_path);
cue_dir = dirname(cue_path_copy);
fd = open(cue_path, O_RDONLY);
if (fd < 0) {
LOG_WARN("Could not open CUE file '%s': %s", cue_path,
strerror(errno));
rv = -errno;
goto free_path_copy;
}
LOG_DEBUG("Parsing CUE file '%s'...", cue_path);
while (get_token(fd, tmp_token, MAX_TOKEN_LEN) > 0) {
if (strcmp(tmp_token, "FILE") == 0) {
get_token(fd, tmp_token, MAX_TOKEN_LEN);
snprintf(track_path, max_len, "%s/%s",
cue_dir, tmp_token);
} else if (strcasecmp(tmp_token, "TRACK") == 0) {
get_token(fd, tmp_token, MAX_TOKEN_LEN);
get_token(fd, tmp_token, MAX_TOKEN_LEN);
if (strcasecmp(tmp_token, "AUDIO") == 0) {
continue;
}
find_token(fd, "INDEX");
get_token(fd, tmp_token, MAX_TOKEN_LEN);
get_token(fd, tmp_token, MAX_TOKEN_LEN);
if (sscanf(tmp_token, "%02d:%02d:%02d", &m, &s, &f) < 3) {
LOG_WARN("Error parsing time stamp '%s'", tmp_token);
return -errno;
}
*offset = ((m * 60) * (s * 75) * f) * 25;
LOG_DEBUG("Found 1st data track on file '%s+%d'",
track_path, *offset);
rv = 0;
goto clean;
}
}
rv = -EINVAL;
clean:
close(fd);
free_path_copy:
free(cue_path_copy);
return rv;
}
static int find_ps1_canonical_name(const char* game_id, char* game_name,
size_t max_len) {
int fd;
char tmp_token[MAX_TOKEN_LEN];
int rv = 0;
fd = open("cddb/ps1.idlst", O_RDONLY);
if (fd < 0) {
LOG_WARN("Could not open id list: %s", strerror(errno));
return -errno;
}
while (get_token(fd, tmp_token, MAX_TOKEN_LEN) > 0) {
if(strcasecmp(tmp_token, game_id) != 0) {
get_token(fd, tmp_token, max_len);
continue;
}
if ((rv = get_token(fd, game_name, max_len)) < 0) {
goto clean;
}
rv = 0;
goto clean;
}
rv = -ENOENT;
clean:
close(fd);
return rv;
}
static int detect_ps1_game(const char* track_path, off_t offset,
char* game_name, size_t max_len) {
int rv;
char buff[4096];
char* pattern = "cdrom:";
char* c;
char* id_start;
int i;
int fd = open(track_path, O_RDONLY);
if (fd < 0) {
LOG_DEBUG("Could not open data track: %s", strerror(errno));
return -errno;
}
if (pread(fd, buff, 10, 0x9340) > 0) {
buff[10] = '\0';
buff[4] = '-';
LOG_DEBUG("Found disk label '%s'", buff);
rv = find_ps1_canonical_name(buff, game_name, max_len);
if (rv == 0) {
goto clean;
}
}
c = pattern;
while (1) {
rv = read(fd, buff, 4096);
if (rv < 0) {
rv = -errno;
goto clean;
}
for(i = 0; i < 4096; i++) {
if (*c == buff[i]) {
c++;
} else {
c = pattern;
continue;
}
if (*c == '\0') {
id_start = &buff[i] + 1;
*strchr(id_start, ';') = '\0';
c = strrchr(id_start, '\\') + 1;
if (c != NULL) {
id_start = c;
}
id_start[4] = '-';
id_start[8] = id_start[9];
id_start[9] = id_start[10];
id_start[10] = '\0';
LOG_DEBUG("Found ps1 id %s", id_start);
rv = find_ps1_canonical_name(id_start, game_name, max_len);
goto clean;
}
}
}
rv = -EINVAL;
clean:
close(fd);
return rv;
}
static int detect_system(const char* track_path, off_t offset,
char** system_name) {
int rv;
char magic[MAGIC_LEN];
int fd;
struct MagicEntry entry;
int i;
fd = open(track_path, O_RDONLY);
if (fd < 0) {
LOG_WARN("Could not open data track of file '%s': %s",
track_path, strerror(errno));
rv = -errno;
goto clean;
}
if (pread(fd, magic, MAGIC_LEN, offset) < MAGIC_LEN) {
LOG_WARN("Could not read data from file '%s' at offset %d: %s",
track_path, offset, strerror(errno));
rv = -errno;
goto clean;
}
LOG_DEBUG("Comparing with known magic numbers...");
for (i = 0; MAGIC_NUMBERS[i].system_name != NULL; i++) {
if (memcmp(MAGIC_NUMBERS[i].magic, magic, MAGIC_LEN) == 0) {
*system_name = MAGIC_NUMBERS[i].system_name;
rv = 0;
goto clean;
}
}
LOG_WARN("Could not find compatible system");
rv = -EINVAL;
clean:
close(fd);
return rv;
}
int find_fist_cue(const char* m3u_path, char* cue_path, size_t max_len) {
char c;
int skip = 0;
int midstream = 0;
char tmp_path[PATH_MAX];
int fd = open(m3u_path, O_RDONLY);
if (fd < 0) {
LOG_WARN("Could not open m3u '%s': %s", m3u_path, strerror(errno));
return -errno;
}
strncpy(tmp_path, m3u_path, PATH_MAX);
strcpy(cue_path, dirname(tmp_path));
cue_path += strlen(cue_path);
cue_path[0] = '/';
cue_path++;
while ((read(fd, &c, 1) > 0)) {
switch (c) {
case '#':
if (!midstream) {
skip = 1;
}
break;
case '\n':
if (skip) {
skip = 0;
} else if(midstream) {
cue_path[0] = '\0';
return 0;
}
break;
case ' ':
if (!midstream) {
break;
}
default:
if (!skip) {
midstream = 1;
cue_path[0] = c;
cue_path++;
}
}
}
return -EINVAL;
}
int detect_cd_game(const char* target_path, char* game_name, size_t max_len) {
char cue_path[PATH_MAX];
char track_path[PATH_MAX];
off_t offset;
char* system_name;
int rv;
if (strcasecmp(target_path + strlen(target_path) - 4, ".m3u") == 0) {
rv = find_fist_cue(target_path, cue_path, PATH_MAX);
if (rv < 0) {
LOG_WARN("Could not parse m3u: %s", strerror(-rv));
return rv;
}
} else {
strncpy(cue_path, target_path, max_len);
}
rv = find_first_data_track(cue_path, &offset, track_path, PATH_MAX);
if (rv < 0) {
LOG_WARN("Could not find valid data track: %s", strerror(-rv));
return rv;
}
LOG_DEBUG("Reading 1st data track...");
if ((rv = detect_system(track_path, offset, &system_name)) < 0) {
return rv;
}
LOG_DEBUG("Detected %s media", system_name);
snprintf(game_name, max_len, "%s.", system_name);
game_name += strlen(system_name) + 1;
max_len -= strlen(system_name) + 1;
if (strcmp(system_name, "ps1") == 0) {
if (detect_ps1_game(track_path, offset, game_name, max_len) == 0) {
return 0;
}
}
snprintf(game_name, max_len, "<unknown>", system_name);
return 0;
}

View File

@ -0,0 +1,3 @@
#include <unistd.h>
int detect_cd_game(const char* cue_path, char* game_name, size_t max_len);

View File

@ -0,0 +1,243 @@
# PS1 Dualanalog Games ;
"ps1.007 Racing*" mednafen-psx dualanalog ;
"ps1.007 - The World Is Not Enough*" mednafen-psx dualanalog ;
"ps1.007 - Tomorrow Never Dies*" mednafen-psx dualanalog ;
"ps1.40 Winks*" mednafen-psx dualanalog ;
"ps1.Ace Combat*" mednafen-psx dualanalog ;
"ps1.Aces of the Air*" mednafen-psx dualanalog ;
"ps1.Action Bass*" mednafen-psx dualanalog ;
"ps1.Action Man - Operation Extreme*" mednafen-psx dualanalog ;
"ps1.Advanced V.G. 2*" mednafen-psx dualanalog ;
"ps1.Aironauts*" mednafen-psx dualanalog ;
"ps1.Air Race Championship*" mednafen-psx dualanalog ;
"ps1.Akuji - The Heartless*" mednafen-psx dualanalog ;
"ps1.Alfred Chicken*" mednafen-psx dualanalog ;
"ps1.Alien la Resurrection*" mednafen-psx dualanalog ;
"ps1.Alien Resurrection*" mednafen-psx dualanalog ;
"ps1.Alone In The Dark - The New Nightmare*" mednafen-psx dualanalog ;
"ps1.Alundra 2*" mednafen-psx dualanalog ;
"ps1.Amazing Virtual Sea Monkeys*" mednafen-psx dualanalog ;
"ps1.Animorphs - Shattered Reality*" mednafen-psx dualanalog ;
"ps1.Ape Escape*" mednafen-psx dualanalog ;
"ps1.Apocalypse*" mednafen-psx dualanalog ;
"ps1.Aqua GT*" mednafen-psx dualanalog ;
"ps1.Armored Core - Master of Arena*" mednafen-psx dualanalog ;
"ps1.Armorines - Project Swarm*" mednafen-psx dualanalog ;
"ps1.Army Men 3D*" mednafen-psx dualanalog ;
"ps1.Army Men - Air Attack 2*" mednafen-psx dualanalog ;
"ps1.Army Men - Air Attack*" mednafen-psx dualanalog ;
"ps1.Army Men - Green Rouge*" mednafen-psx dualanalog ;
"ps1.Army Men - Sarge's Heroes 2*" mednafen-psx dualanalog ;
"ps1.Army Men - Sarge's Heroes*" mednafen-psx dualanalog ;
"ps1.Army Men - World War*" mednafen-psx dualanalog ;
"ps1.Assault Retribution*" mednafen-psx dualanalog ;
"ps1.Asterix - Mega Madness*" mednafen-psx dualanalog ;
"ps1.Asteroids*" mednafen-psx dualanalog ;
"ps1.Barbie Explorer" mednafen-psx dualanalog ;
"ps1.Barbie - Gotta Have Games" mednafen-psx dualanalog ;
"ps1.Batman Beyond - Return of the Joker" mednafen-psx dualanalog ;
"ps1.Batman & Robin" mednafen-psx dualanalog ;
"ps1.Beatmania" mednafen-psx dualanalog ;
"ps1.Beat Planet Music" mednafen-psx dualanalog ;
"ps1.Beyblade" mednafen-psx dualanalog ;
"ps1.Big Bass Fishing" mednafen-psx dualanalog ;
"ps1.BioHazard - Director's Cut - Dual Shock*" mednafen-psx dualanalog ;
"ps1.Bishi Bashi Special" mednafen-psx dualanalog ;
"ps1.Blade" mednafen-psx dualanalog ;
"ps1.Blasto!" mednafen-psx dualanalog ;
"ps1.Blast Radius" mednafen-psx dualanalog ;
"ps1.Block Kuzushi" mednafen-psx dualanalog ;
"ps1.Block Wars" mednafen-psx dualanalog ;
"ps1.Bloody Roar 2*" mednafen-psx dualanalog ;
"ps1.Bob the Builder - Can We Fix It" mednafen-psx dualanalog ;
"ps1.Boombots" mednafen-psx dualanalog ;
"ps1.Bratz" mednafen-psx dualanalog ;
"ps1.Brave Fencer Musashi*" mednafen-psx dualanalog ;
"ps1.Breakout" mednafen-psx dualanalog ;
"ps1.Buggy" mednafen-psx dualanalog ;
"ps1.Bugs Bunny - Lost in Time*" mednafen-psx dualanalog ;
"ps1.Bugs Bunny & Taz - Time Busters" mednafen-psx dualanalog ;
"ps1.C-12 - The Final Resistance" mednafen-psx dualanalog ;
"ps1.Captain Commando" mednafen-psx dualanalog ;
"ps1.Cardinal Syn*" mednafen-psx dualanalog ;
"ps1.Carmageddon*" mednafen-psx dualanalog ;
"ps1.Casper - Friends Around The World" mednafen-psx dualanalog ;
"ps1.Chaos Break" mednafen-psx dualanalog ;
"ps1.Chicken Run" mednafen-psx dualanalog ;
"ps1.Chippoke Ralph no Daibouken (Adventure of Little Ralph)" mednafen-psx dualanalog ;
"ps1.Chocobo Racing" mednafen-psx dualanalog ;
"ps1.Chrono Cross*" pcsxr dualanalog ;
"ps1.Colin Mc[rR]rae*" mednafen-psx dualanalog ;
"ps1.Colony Wars*" mednafen-psx dualanalog ;
"ps1.Cosmowarrior Rei" mednafen-psx dualanalog ;
"ps1.Cowboy Bebop" mednafen-psx dualanalog ;
"ps1.Crash Bandicoot 2*" mednafen-psx dualanalog ;
"ps1.Crash Bandicoot 3*" mednafen-psx dualanalog ;
"ps1.Crash Bandicoot Carnival - Crash Bash*" mednafen-psx dualanalog ;
"ps1.Crash Bash Demo" mednafen-psx dualanalog ;
"ps1.Crash Bash" mednafen-psx dualanalog ;
"ps1.Crash Bash" mednafen-psx dualanalog ;
"ps1.Crash Bash & Spyro - Year of the Dragon Demo" mednafen-psx dualanalog ;
"ps1.Crash Team Racing*" mednafen-psx dualanalog multitap ;
"ps1.Crisis Beat" mednafen-psx dualanalog ;
"ps1.Croc 2*" mednafen-psx dualanalog ;
"ps1.Croc - Legend of the Gobbos" mednafen-psx dualanalog ;
"ps1.C - The Contra Adventure" mednafen-psx dualanalog ;
"ps1.CT Special Forces" mednafen-psx dualanalog ;
"ps1.CyberTiger Golf" mednafen-psx dualanalog ;
"ps1.Fighting Force*" mednafen-psx dualanalog ;
"ps1.Future Cop*" mednafen-psx dualanalog ;
"ps1.Legacy of Kain - Soul Reaver*" pcsxr dualanalog ;
"ps1.Saru! Get You!*" mednafen-psx dualanalog ;
"ps1.Simple Characters 2000 Series #02 - Afro Inu - The Puzzle*" mednafen-psx dualanalog ;
"ps1.Soul Reaver - Legacy of Kain*" mednafen-psx dualanalog ;
"ps1.Spyro the Dragon*"" mednafen-psx dualanalog ;
"ps1.Spyro - Year of the Dragon*" mednafen-psx dualanalog ;
"ps1.Vagrant Story*" mednafen-psx dualanalog ;
"ps1.Earthworm Jim 2" pcsxr ;
"ps1.Mickey's Wild Adventures" pcsxr ;
# SNES BSNES Performance ;
"snes.
"ps1.Earthworm Jim 2" pcsxr ;
"ps1.Mickey's Wild Adventures" pcsxr ;
# SNES BSNES Performance ;
"snes.Psycho Dream*" bsnes-performance ;
# SNES Multitap Games ;
"snes.Bakukyuu Renpatsu!! Super B-Daman*" snes9x multitap ;
"snes.Bakutou Dochers - Bumps-jima wa Oosawagi*" snes9x multitap ;
"snes.Barkley Shut Up and Jam!*" snes9x multitap ;
"snes.Barkley Shut Up and Jam!*" snes9x multitap ;
"snes.Battle Cross*" snes9x multitap ;
"snes.Battle Jockey*" snes9x multitap ;
"snes.Bill Walsh College Football*" snes9x multitap ;
"snes.Capcom's Soccer Shootout*" snes9x multitap ;
"snes.College Slam*" snes9x multitap ;
"snes.Crystal Beans From Dungeon Explorer*" snes9x multitap ;
"snes.Dragon - The Bruce Lee Story*" snes9x multitap ;
"snes.Dream Basketball - Dunk and Hoop*" snes9x multitap ;
"snes.Dynamic Stadium*" snes9x multitap ;
"snes.ESPN National Hockey Night*" snes9x multitap ;
"snes.FIFA 98*" snes9x multitap ;
"snes.FIFA International Soccer*" snes9x multitap ;
"snes.FIFA Soccer 96*" snes9x multitap ;
"snes.FIFA Soccer 97*" snes9x multitap ;
"snes.Final Set*" snes9x multitap ;
"snes.Fire Striker*" snes9x multitap ;
"snes.From TV Animation Slam Dunk - SD Heat Up!!*" snes9x multitap ;
"snes.Go! Go! Dodge League*" snes9x multitap ;
"snes.Hammerlock Wrestling*" snes9x multitap ;
"snes.Hat Trick Hero 2*" snes9x multitap ;
"snes.Head-On Soccer*" snes9x multitap ;
"snes.Hebereke no Oishii Puzzle ha Irimasenka*" snes9x multitap ;
"snes.Human Grand Prix III - F1 Triple Battle*" snes9x multitap ;
"snes.Human Grand Prix IV - F1 Dream Battle*" snes9x multitap ;
"snes.Hungry Dinosaurs*" snes9x multitap ;
"snes.International Superstar Soccer Deluxe*" snes9x multitap ;
"snes.J.League Excite Stage '94*" snes9x multitap ;
"snes.J.League Excite Stage '95*" snes9x multitap ;
"snes.J.League Excite Stage '96*" snes9x multitap ;
"snes.J.League Super Soccer '95*" snes9x multitap ;
"snes.J.League Super Soccer*" snes9x multitap ;
"snes.JWP Joshi Pro Wrestling - Pure Wrestle Queens*" snes9x multitap ;
"snes.Jikkyou Power Pro Wrestling '96*" snes9x multitap ;
"snes.Jimmy Connors Pro Tennis Tour*" snes9x multitap ;
"snes.Kunio-kun no Dodge Ball dayo Zenin Shuugou!*" snes9x multitap ;
"snes.Looney Tunes Basketball*" snes9x multitap ;
"snes.Madden NFL '94*" snes9x multitap ;
"snes.Madden NFL '95*" snes9x multitap ;
"snes.Madden NFL '96*" snes9x multitap ;
"snes.Madden NFL '97*" snes9x multitap ;
"snes.Madden NFL '98*" snes9x multitap ;
"snes.Micro Machines 2 - Turbo Tournament*" snes9x multitap ;
"snes.Micro Machines*" snes9x multitap ;
"snes.Mizuki Shigeru no Youkai Hyakkiyakou*" snes9x multitap ;
"snes.Multi Play Volleyball*" snes9x multitap ;
"snes.NBA Give 'N Go*" snes9x multitap ;
"snes.NBA Hang Time*" snes9x multitap ;
"snes.NBA Jam - Tournament Edition*" snes9x multitap ;
"snes.NBA Jam*" snes9x multitap ;
"snes.NBA Live 95*" snes9x multitap ;
"snes.NBA Live 96*" snes9x multitap ;
"snes.NBA Live 97*" snes9x multitap ;
"snes.NBA Live 98*" snes9x multitap ;
"snes.NCAA Final Four Basketball*" snes9x multitap ;
"snes.NCAA Football*" snes9x multitap ;
"snes.NFL Quarterback Club 96*" snes9x multitap ;
"snes.NFL Quarterback Club*" snes9x multitap ;
"snes.NHL '94*" snes9x multitap ;
"snes.NHL '98*" snes9x multitap ;
"snes.NHL Pro Hockey '94*" snes9x multitap ;
"snes.Natsume Championship Wrestling*" snes9x multitap ;
"snes.Peace Keepers, The*" snes9x multitap ;
"snes.Pieces*" snes9x multitap ;
"snes.Rap Jam - Volume One*" snes9x multitap ;
"snes.Saturday Night Slam Masters*" snes9x multitap ;
"snes.Secret of Mana*" snes9x multitap ;
"snes.Shin Nippon Pro Wrestling '94 - Battlefield in Tokyo Dome*" snes9x multitap ;
"snes.Shin Nippon Pro Wrestling - Chou Senshi in Tokyo Dome*" snes9x multitap ;
"snes.Shin Nippon Pro Wrestling Kounin '95 - Tokyo Dome Battle 7*" snes9x multitap ;
"snes.Smash Tennis*" snes9x multitap ;
"snes.Sporting News, The - Power Baseball*" snes9x multitap ;
"snes.Sterling Sharpe End 2 End*" snes9x multitap ;
"snes.Street Hockey '95*" snes9x multitap ;
"snes.Street Racer*" snes9x multitap ;
"snes.Sugoi Hebereke*" snes9x multitap ;
"snes.Sugoro Quest++ Dicenics*" snes9x multitap ;
"snes.Super Bomberman - Panic Bomber W*" snes9x multitap ;
"snes.Super Bomberman 2*" snes9x multitap ;
"snes.Super Bomberman 3*" snes9x multitap ;
"snes.Super Bomberman 4*" snes9x multitap ;
"snes.Super Bomberman 5*" snes9x multitap ;
"snes.Super Bomberman*" snes9x multitap ;
"snes.Super Fire Pro Wrestling - Queen's Special*" snes9x multitap ;
"snes.Super Fire Pro Wrestling Special*" snes9x multitap ;
"snes.Super Fire Pro Wrestling X Premium*" snes9x multitap ;
"snes.Super Fire Pro Wrestling X*" snes9x multitap ;
"snes.Super Formation Soccer 94 - World Cup Final Data*" snes9x multitap ;
"snes.Super Formation Soccer 94*" snes9x multitap ;
"snes.Super Formation Soccer 95 della Serie A - UCC Xaqua*" snes9x multitap ;
"snes.Super Formation Soccer 95 della Serie A*" snes9x multitap ;
"snes.Super Formation Soccer 96*" snes9x multitap ;
"snes.Super Formation Soccer II*" snes9x multitap ;
"snes.Super Ice Hockey*" snes9x multitap ;
"snes.Super Kyousouba - Kaze no Sylphid*" snes9x multitap ;
"snes.Super Power League*" snes9x multitap ;
"snes.Super Tekkyuu Fight!*" snes9x multitap ;
"snes.Super Tetris 3*" snes9x multitap ;
"snes.Syndicate*" snes9x multitap ;
"snes.Tenryu Genichiro no Pro Wrestling Revolution*" snes9x multitap ;
"snes.Tiny Toon Adventures - Wild & Wacky Sports*" snes9x multitap ;
"snes.Top Gear 3000*" snes9x multitap ;
"snes.Turbo Toons*" snes9x multitap ;
"snes.Virtual Soccer*" snes9x multitap ;
"snes.Vs. Collection*" snes9x multitap ;
"snes.WWF Raw*" snes9x multitap ;
"snes.Yuujin no Furi Furi Girls*" snes9x multitap ;
"snes.Zero 4 Champ RR-Z*" snes9x multitap ;
"snes.Zero 4 Champ RR*" snes9x multitap ;
# Defaults ;
"gba.*" vba ;
"gbc.*" gambatte ;
"gb.*" gambatte ;
"gg.*" genplus ;
"nes.*" fceu ;
"pcecd.*" mednafen-pce-fast ;
"pce.*" mednafen-pce-fast ;
"ps1.*" mednafen-psx ;
"wswan.*" mednafen-wswan ;
"a26.*" stella ;
"scd.*" genplus ;
"sg1000.*" genplus ;
"smd.*" genplus ;
"sms.*" genplus ;
"snes.*" snes9x ;
"nds.*" desmume ;

6
tools/retrolaunch/log.h Normal file
View File

@ -0,0 +1,6 @@
#include <stdio.h>
#define LOG(stream, level, msg, ...) fprintf(stream, "%s::%s+%d::", level, __FILE__, __LINE__); fprintf(stream, msg, ##__VA_ARGS__); fprintf(stream, "\n")
#define LOG_DEBUG(msg, ...) LOG(stderr, "DEBUG", msg, ##__VA_ARGS__)
#define LOG_WARN(msg, ...) LOG(stderr, "WARNING", msg, ##__VA_ARGS__)
#define LOG_INFO(msg, ...) LOG(stdout, "INFO", msg, ##__VA_ARGS__)

318
tools/retrolaunch/main.c Normal file
View File

@ -0,0 +1,318 @@
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <libgen.h>
#include <limits.h>
#include "sha1.h"
#include "parser.h"
#include "cd_detect.h"
#include "rl_fnmatch.h"
#include "../../file.h"
#include "log.h"
#define SHA1_LEN 40
#define HASH_LEN SHA1_LEN
static int find_hash(int fd, const char *hash, char *game_name, size_t max_len)
{
char token[MAX_TOKEN_LEN];
while (1) {
if (find_token(fd, "game") < 0) {
return -1;
}
if (find_token(fd, "name") < 0) {
return -1;
}
if (get_token(fd, game_name, max_len) < 0) {
return -1;
}
if (find_token(fd, "sha1") < 0) {
return -1;
}
if (get_token(fd, token, MAX_TOKEN_LEN) < 0) {
return -1;
}
if (strcasecmp(hash, token) == 0) {
return 0;
}
}
}
static int
find_rom_canonical_name(const char *hash, char *game_name, size_t max_len)
{
// TODO: Error handling
int i;
int rv;
int fd;
int offs;
char *dat_path;
char *dat_name;
struct string_list *files;
files = dir_list_new("db", "dat", false);
if (!files) {
return -1;
}
for (i = 0; i < files->size; i++) {
dat_path = files->elems[i].data;
dat_name = basename(dat_path);
offs = strchr(dat_name, '.') - dat_name + 1;
memcpy(game_name, dat_name, offs);
fd = open(dat_path, O_RDONLY);
if (fd < 0) {
continue;
}
if (find_hash(fd, hash, game_name + offs, max_len - offs) == 0) {
rv = 0;
close(fd);
goto clean;
}
close(fd);
}
rv = -1;
clean:
dir_list_free(files);
return rv;
}
static int get_sha1(const char *path, char *result)
{
int fd;
int rv;
int buff_len = 4096;
unsigned char buff[buff_len];
SHA1Context sha;
fd = open(path, O_RDONLY);
if (fd < 0) {
return -errno;
}
SHA1Reset(&sha);
rv = 1;
while (rv > 0) {
rv = read(fd, buff, buff_len);
if (rv < 0) {
close(fd);
return -errno;
}
SHA1Input(&sha, buff, rv);
}
if (!SHA1Result(&sha)) {
return -1;
}
sprintf(result, "%08X%08X%08X%08X%08X",
sha.Message_Digest[0],
sha.Message_Digest[1],
sha.Message_Digest[2],
sha.Message_Digest[3], sha.Message_Digest[4]);
return 0;
}
struct RunInfo {
char core[50];
int multitap;
int dualanalog;
};
static int get_run_info(struct RunInfo *info, char *game_name)
{
int fd = open("./launch.conf", O_RDONLY);
int rv;
char token[MAX_TOKEN_LEN];
if (fd < 0) {
return -errno;
}
memset(info, 0, sizeof(struct RunInfo));
while (1) {
if ((rv = get_token(fd, token, MAX_TOKEN_LEN)) < 0) {
goto clean;
}
if (rl_fnmatch(token, game_name, 0) != 0) {
if ((rv = find_token(fd, ";")) < 0) {
goto clean;
}
continue;
}
LOG_DEBUG("Matched rule '%s'", token);
if ((rv = get_token(fd, token, MAX_TOKEN_LEN)) < 0) {
goto clean;
}
break;
}
strncpy(info->core, token, 50);
info->multitap = 0;
info->dualanalog = 0;
if ((rv = get_token(fd, token, MAX_TOKEN_LEN)) < 0) {
goto clean;
}
while (strcmp(token, ";") != 0) {
if (strcmp(token, "multitap") == 0) {
info->multitap = 1;
} else if (strcmp(token, "dualanalog") == 0) {
info->dualanalog = 1;
}
if ((rv = get_token(fd, token, MAX_TOKEN_LEN)) < 0) {
goto clean;
}
}
rv = 0;
clean:
close(fd);
return rv;
}
char *SUFFIX_MATCH[] = {
".a26", "a26",
".bin", "smd",
".gba", "gba",
".gbc", "gbc",
".gb", "gb",
".gen", "smd",
".gg", "gg",
".nds", "nds",
".nes", "nes",
".pce", "pce",
".sfc", "snes",
".smc", "snes",
".smd", "smd",
".sms", "sms",
".wsc", "wswan",
NULL
};
static int detect_rom_game(const char *path, char *game_name, size_t max_len)
{
char hash[HASH_LEN + 1];
int rv;
char *suffix = strrchr(path, '.');
char **tmp_suffix;
if ((rv = get_sha1(path, hash)) < 0) {
LOG_WARN("Could not calculate hash: %s", strerror(-rv));
}
if (find_rom_canonical_name(hash, game_name, max_len) < 0) {
LOG_DEBUG("Could not detect rom with hash `%s` guessing", hash);
for (tmp_suffix = SUFFIX_MATCH; *tmp_suffix != NULL;
tmp_suffix += 2) {
if (strcasecmp(suffix, *tmp_suffix) == 0) {
snprintf(game_name, max_len, "%s.<unknown>",
*(tmp_suffix + 1));
return 0;
}
}
return -EINVAL;
}
return 0;
}
static int detect_game(const char *path, char *game_name, size_t max_len)
{
if ((strcasecmp(path + strlen(path) - 4, ".cue") == 0) ||
(strcasecmp(path + strlen(path) - 4, ".m3u") == 0)) {
LOG_INFO("Starting CD game detection...");
return detect_cd_game(path, game_name, max_len);
} else {
LOG_INFO("Starting rom game detection...");
return detect_rom_game(path, game_name, max_len);
}
}
static int run_retroarch(const char *path, const struct RunInfo *info)
{
char core_path[PATH_MAX];
sprintf(core_path, "./cores/libretro-%s.so", info->core);
char *retro_argv[30] = { "retroarch",
"-L", core_path
};
int argi = 3;
if (info->multitap) {
retro_argv[argi] = "-4";
argi++;
LOG_INFO("Game supports multitap");
}
if (info->dualanalog) {
retro_argv[argi] = "-A";
argi++;
retro_argv[argi] = "1";
argi++;
retro_argv[argi] = "-A";
argi++;
retro_argv[argi] = "2";
argi++;
LOG_INFO("Game supports the dualshock controller");
}
retro_argv[argi] = strdup(path);
argi++;
retro_argv[argi] = NULL;
execvp(retro_argv[0], retro_argv);
return -errno;
}
int main(int argc, char *argv[])
{
if (argc < 2) {
return -1;
}
char game_name[MAX_TOKEN_LEN];
char *path = argv[1];
struct RunInfo info;
int rv;
LOG_INFO("Analyzing '%s'", path);
if ((rv = detect_game(path, game_name, MAX_TOKEN_LEN)) < 0) {
LOG_WARN("Could not detect game: %s", strerror(-rv));
return -rv;
}
LOG_INFO("Game is `%s`", game_name);
if ((rv = get_run_info(&info, game_name)) < 0) {
LOG_WARN("Could not find sutable core: %s", strerror(-rv));
return -1;
}
LOG_DEBUG("Usinge libretro core '%s'", info.core);
LOG_INFO("Launching '%s'", path);
rv = run_retroarch(path, &info);
LOG_WARN("Could not launch retroarch: %s", strerror(-rv));
return -rv;
}
// Stub just so that it compiles
void rarch_init_msg_queue(void) {}

View File

@ -0,0 +1,72 @@
#include "parser.h"
#include <errno.h>
#include <string.h>
#include <stdlib.h>
ssize_t get_token(int fd, char *token, size_t max_len)
{
char *c = token;
int rv;
ssize_t len = 0;
int in_string = 0;
while (1) {
rv = read(fd, c, 1);
if (rv == 0) {
return 0;
} else if (rv < 1) {
switch (errno) {
case EINTR:
case EAGAIN:
continue;
default:
return -errno;
}
}
switch (*c) {
case ' ':
case '\t':
case '\r':
case '\n':
if (c == token) {
continue;
}
if (!in_string) {
*c = '\0';
return len;
}
break;
case '\"':
if (c == token) {
in_string = 1;
continue;
}
*c = '\0';
return len;
}
len++;
c++;
if (len == max_len) {
*c = '\0';
return len;
}
}
}
int find_token(int fd, char *token)
{
int tmp_len = strlen(token);
char *tmp_token = calloc(tmp_len, sizeof(char));
while (strncmp(tmp_token, token, tmp_len) != 0) {
if (get_token(fd, tmp_token, tmp_len) <= 0) {
return -1;
}
}
return 0;
}

View File

@ -0,0 +1,6 @@
#include <unistd.h>
#define MAX_TOKEN_LEN 255
ssize_t get_token(int fd, char* token, size_t max_len);
int find_token(int fd, char* token);

View File

@ -0,0 +1,132 @@
#if __TEST_FNMATCH__
#include <assert.h>
#endif
#include "rl_fnmatch.h"
// Implemnentation of fnmatch(3) so it can be distributed to non *nix platforms
// No flags are implemented ATM. We don't use them. Add flags as needed.
int rl_fnmatch(const char *pattern, const char *string, int flags) {
const char *c;
int charmatch = 0;
int rv;
for (c = pattern; *c != '\0'; c++) {
// String ended before pattern
if ((*c != '*') && (*string == '\0')) {
return FNM_NOMATCH;
}
switch (*c) {
// Match any number of unknown chars
case '*':
// Find next node in the pattern ignoring multiple
// asterixes
do {
c++;
if (*c == '\0') {
return 0;
}
} while (*c == '*');
// Match the remaining pattern ingnoring more and more
// chars.
do {
// We reached the end of the string without a
// match. There is a way to optimize this by
// calculating the minimum chars needed to
// match the remaining pattern but I don't
// think it is worth the work ATM.
if (*string == '\0') {
return FNM_NOMATCH;
}
rv = rl_fnmatch(c, string, flags);
string++;
} while (rv != 0);
return 0;
// Match char from list
case '[':
charmatch = 0;
for (c++; *c != ']'; c++) {
// Bad formath
if (*c == '\0') {
return FNM_NOMATCH;
}
// Match already found
if (charmatch) {
continue;
}
if (*c == *string) {
charmatch = 1;
}
}
// No match in list
if (!charmatch) {
return FNM_NOMATCH;
}
string++;
break;
// Has any char
case '?':
string++;
break;
// Match following char verbatim
case '\\':
c++;
// Dangling escape at end of pattern
if (c == '\0') {
return FNM_NOMATCH;
}
default:
if (*c != *string) {
return FNM_NOMATCH;
}
string++;
}
}
// End of string and end of pattend
if (*string == '\0') {
return 0;
} else {
return FNM_NOMATCH;
}
}
#if __TEST_FNMATCH__
int main() {
assert(rl_fnmatch("TEST", "TEST", 0) == 0);
assert(rl_fnmatch("TE?T", "TEST", 0) == 0);
assert(rl_fnmatch("TE[Ssa]T", "TEST", 0) == 0);
assert(rl_fnmatch("TE[Ssda]T", "TEsT", 0) == 0);
assert(rl_fnmatch("TE[Ssda]T", "TEdT", 0) == 0);
assert(rl_fnmatch("TE[Ssda]T", "TEaT", 0) == 0);
assert(rl_fnmatch("TEST*", "TEST", 0) == 0);
assert(rl_fnmatch("TEST**", "TEST", 0) == 0);
assert(rl_fnmatch("TE*ST*", "TEST", 0) == 0);
assert(rl_fnmatch("TE**ST*", "TEST", 0) == 0);
assert(rl_fnmatch("TE**ST*", "TExST", 0) == 0);
assert(rl_fnmatch("TE**ST", "TEST", 0) == 0);
assert(rl_fnmatch("TE**ST", "TExST", 0) == 0);
assert(rl_fnmatch("TE\\**ST", "TE*xST", 0) == 0);
assert(rl_fnmatch("*.*", "test.jpg", 0) == 0);
assert(rl_fnmatch("*.jpg", "test.jpg", 0) == 0);
assert(rl_fnmatch("*.[Jj][Pp][Gg]", "test.jPg", 0) == 0);
assert(rl_fnmatch("*.[Jj]*[Gg]", "test.jPg", 0) == 0);
assert(rl_fnmatch("TEST?", "TEST", 0) == FNM_NOMATCH);
assert(rl_fnmatch("TES[asd", "TEST", 0) == FNM_NOMATCH);
assert(rl_fnmatch("TEST\\", "TEST", 0) == FNM_NOMATCH);
assert(rl_fnmatch("TEST*S", "TEST", 0) == FNM_NOMATCH);
assert(rl_fnmatch("TE**ST", "TExT", 0) == FNM_NOMATCH);
assert(rl_fnmatch("TE\\*T", "TExT", 0) == FNM_NOMATCH);
assert(rl_fnmatch("TES?", "TES", 0) == FNM_NOMATCH);
assert(rl_fnmatch("TE", "TEST", 0) == FNM_NOMATCH);
assert(rl_fnmatch("TEST!", "TEST", 0) == FNM_NOMATCH);
assert(rl_fnmatch("DSAD", "TEST", 0) == FNM_NOMATCH);
}
#endif

View File

@ -0,0 +1,8 @@
#ifndef __RL_FNMATCH_H__
#define __RL_FNMATCH_H__
#define FNM_NOMATCH 1
int rl_fnmatch(const char *pattern, const char *string, int flags);
#endif

371
tools/retrolaunch/sha1.c Normal file
View File

@ -0,0 +1,371 @@
/*
* sha1.c
*
* Copyright (C) 1998, 2009
* Paul E. Jones <paulej@packetizer.com>
* All Rights Reserved
*
*****************************************************************************
* $Id: sha1.c 12 2009-06-22 19:34:25Z paulej $
*****************************************************************************
*
* Description:
* This file implements the Secure Hashing Standard as defined
* in FIPS PUB 180-1 published April 17, 1995.
*
* The Secure Hashing Standard, which uses the Secure Hashing
* Algorithm (SHA), produces a 160-bit message digest for a
* given data stream. In theory, it is highly improbable that
* two messages will produce the same message digest. Therefore,
* this algorithm can serve as a means of providing a "fingerprint"
* for a message.
*
* Portability Issues:
* SHA-1 is defined in terms of 32-bit "words". This code was
* written with the expectation that the processor has at least
* a 32-bit machine word size. If the machine word size is larger,
* the code should still function properly. One caveat to that
* is that the input functions taking characters and character
* arrays assume that only 8 bits of information are stored in each
* character.
*
* Caveats:
* SHA-1 is designed to work with messages less than 2^64 bits
* long. Although SHA-1 allows a message digest to be generated for
* messages of any number of bits less than 2^64, this
* implementation only works with messages with a length that is a
* multiple of the size of an 8-bit character.
*
*/
#include "sha1.h"
/*
* Define the circular shift macro
*/
#define SHA1CircularShift(bits,word) \
((((word) << (bits)) & 0xFFFFFFFF) | \
((word) >> (32-(bits))))
/* Function prototypes */
void SHA1ProcessMessageBlock(SHA1Context *);
void SHA1PadMessage(SHA1Context *);
/*
* SHA1Reset
*
* Description:
* This function will initialize the SHA1Context in preparation
* for computing a new message digest.
*
* Parameters:
* context: [in/out]
* The context to reset.
*
* Returns:
* Nothing.
*
* Comments:
*
*/
void SHA1Reset(SHA1Context *context)
{
context->Length_Low = 0;
context->Length_High = 0;
context->Message_Block_Index = 0;
context->Message_Digest[0] = 0x67452301;
context->Message_Digest[1] = 0xEFCDAB89;
context->Message_Digest[2] = 0x98BADCFE;
context->Message_Digest[3] = 0x10325476;
context->Message_Digest[4] = 0xC3D2E1F0;
context->Computed = 0;
context->Corrupted = 0;
}
/*
* SHA1Result
*
* Description:
* This function will return the 160-bit message digest into the
* Message_Digest array within the SHA1Context provided
*
* Parameters:
* context: [in/out]
* The context to use to calculate the SHA-1 hash.
*
* Returns:
* 1 if successful, 0 if it failed.
*
* Comments:
*
*/
int SHA1Result(SHA1Context *context)
{
if (context->Corrupted)
{
return 0;
}
if (!context->Computed)
{
SHA1PadMessage(context);
context->Computed = 1;
}
return 1;
}
/*
* SHA1Input
*
* Description:
* This function accepts an array of octets as the next portion of
* the message.
*
* Parameters:
* context: [in/out]
* The SHA-1 context to update
* message_array: [in]
* An array of characters representing the next portion of the
* message.
* length: [in]
* The length of the message in message_array
*
* Returns:
* Nothing.
*
* Comments:
*
*/
void SHA1Input( SHA1Context *context,
const unsigned char *message_array,
unsigned length)
{
if (!length)
{
return;
}
if (context->Computed || context->Corrupted)
{
context->Corrupted = 1;
return;
}
while(length-- && !context->Corrupted)
{
context->Message_Block[context->Message_Block_Index++] =
(*message_array & 0xFF);
context->Length_Low += 8;
/* Force it to 32 bits */
context->Length_Low &= 0xFFFFFFFF;
if (context->Length_Low == 0)
{
context->Length_High++;
/* Force it to 32 bits */
context->Length_High &= 0xFFFFFFFF;
if (context->Length_High == 0)
{
/* Message is too long */
context->Corrupted = 1;
}
}
if (context->Message_Block_Index == 64)
{
SHA1ProcessMessageBlock(context);
}
message_array++;
}
}
/*
* SHA1ProcessMessageBlock
*
* Description:
* This function will process the next 512 bits of the message
* stored in the Message_Block array.
*
* Parameters:
* None.
*
* Returns:
* Nothing.
*
* Comments:
* Many of the variable names in the SHAContext, especially the
* single character names, were used because those were the names
* used in the publication.
*
*
*/
void SHA1ProcessMessageBlock(SHA1Context *context)
{
const unsigned K[] = /* Constants defined in SHA-1 */
{
0x5A827999,
0x6ED9EBA1,
0x8F1BBCDC,
0xCA62C1D6
};
int t; /* Loop counter */
unsigned temp; /* Temporary word value */
unsigned W[80]; /* Word sequence */
unsigned A, B, C, D, E; /* Word buffers */
/*
* Initialize the first 16 words in the array W
*/
for(t = 0; t < 16; t++)
{
W[t] = ((unsigned) context->Message_Block[t * 4]) << 24;
W[t] |= ((unsigned) context->Message_Block[t * 4 + 1]) << 16;
W[t] |= ((unsigned) context->Message_Block[t * 4 + 2]) << 8;
W[t] |= ((unsigned) context->Message_Block[t * 4 + 3]);
}
for(t = 16; t < 80; t++)
{
W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
}
A = context->Message_Digest[0];
B = context->Message_Digest[1];
C = context->Message_Digest[2];
D = context->Message_Digest[3];
E = context->Message_Digest[4];
for(t = 0; t < 20; t++)
{
temp = SHA1CircularShift(5,A) +
((B & C) | ((~B) & D)) + E + W[t] + K[0];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
for(t = 20; t < 40; t++)
{
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
for(t = 40; t < 60; t++)
{
temp = SHA1CircularShift(5,A) +
((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
for(t = 60; t < 80; t++)
{
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
context->Message_Digest[0] =
(context->Message_Digest[0] + A) & 0xFFFFFFFF;
context->Message_Digest[1] =
(context->Message_Digest[1] + B) & 0xFFFFFFFF;
context->Message_Digest[2] =
(context->Message_Digest[2] + C) & 0xFFFFFFFF;
context->Message_Digest[3] =
(context->Message_Digest[3] + D) & 0xFFFFFFFF;
context->Message_Digest[4] =
(context->Message_Digest[4] + E) & 0xFFFFFFFF;
context->Message_Block_Index = 0;
}
/*
* SHA1PadMessage
*
* Description:
* According to the standard, the message must be padded to an even
* 512 bits. The first padding bit must be a '1'. The last 64
* bits represent the length of the original message. All bits in
* between should be 0. This function will pad the message
* according to those rules by filling the Message_Block array
* accordingly. It will also call SHA1ProcessMessageBlock()
* appropriately. When it returns, it can be assumed that the
* message digest has been computed.
*
* Parameters:
* context: [in/out]
* The context to pad
*
* Returns:
* Nothing.
*
* Comments:
*
*/
void SHA1PadMessage(SHA1Context *context)
{
/*
* Check to see if the current message block is too small to hold
* the initial padding bits and length. If so, we will pad the
* block, process it, and then continue padding into a second
* block.
*/
if (context->Message_Block_Index > 55)
{
context->Message_Block[context->Message_Block_Index++] = 0x80;
while(context->Message_Block_Index < 64)
{
context->Message_Block[context->Message_Block_Index++] = 0;
}
SHA1ProcessMessageBlock(context);
while(context->Message_Block_Index < 56)
{
context->Message_Block[context->Message_Block_Index++] = 0;
}
}
else
{
context->Message_Block[context->Message_Block_Index++] = 0x80;
while(context->Message_Block_Index < 56)
{
context->Message_Block[context->Message_Block_Index++] = 0;
}
}
/*
* Store the message length as the last 8 octets
*/
context->Message_Block[56] = (context->Length_High >> 24) & 0xFF;
context->Message_Block[57] = (context->Length_High >> 16) & 0xFF;
context->Message_Block[58] = (context->Length_High >> 8) & 0xFF;
context->Message_Block[59] = (context->Length_High) & 0xFF;
context->Message_Block[60] = (context->Length_Low >> 24) & 0xFF;
context->Message_Block[61] = (context->Length_Low >> 16) & 0xFF;
context->Message_Block[62] = (context->Length_Low >> 8) & 0xFF;
context->Message_Block[63] = (context->Length_Low) & 0xFF;
SHA1ProcessMessageBlock(context);
}

54
tools/retrolaunch/sha1.h Normal file
View File

@ -0,0 +1,54 @@
/*
* sha1.h
*
* Copyright (C) 1998, 2009
* Paul E. Jones <paulej@packetizer.com>
* All Rights Reserved
*
*****************************************************************************
* $Id: sha1.h 12 2009-06-22 19:34:25Z paulej $
*****************************************************************************
*
* Description:
* This class implements the Secure Hashing Standard as defined
* in FIPS PUB 180-1 published April 17, 1995.
*
* Many of the variable names in the SHA1Context, especially the
* single character names, were used because those were the names
* used in the publication.
*
* Please read the file sha1.c for more information.
*
*/
#ifndef _SHA1_H_
#define _SHA1_H_
/*
* This structure will hold context information for the hashing
* operation
*/
typedef struct SHA1Context
{
unsigned Message_Digest[5]; /* Message Digest (output) */
unsigned Length_Low; /* Message length in bits */
unsigned Length_High; /* Message length in bits */
unsigned char Message_Block[64]; /* 512-bit message blocks */
int Message_Block_Index; /* Index into message block array */
int Computed; /* Is the digest computed? */
int Corrupted; /* Is the message digest corruped? */
} SHA1Context;
/*
* Function Prototypes
*/
void SHA1Reset(SHA1Context *);
int SHA1Result(SHA1Context *);
void SHA1Input( SHA1Context *,
const unsigned char *,
unsigned);
#endif