move tools away to a separate project

This commit is contained in:
Raphael Assenat 2016-01-22 00:03:42 -05:00
parent ddb63cf8e9
commit 8c3e9c8eec
47 changed files with 0 additions and 9434 deletions

14
tools/.gitignore vendored
View File

@ -1,14 +0,0 @@
gui.xml
gcn64cfg.glade~
mempak_convert
mempak_extract_note
mempak_format
mempak_insert_note
mempak_ls
mempak_rm
gcn64ctl
gcn64ctl_gui
*.swp
*.mpk
*.n64
*.exe

View File

@ -1,86 +0,0 @@
CC=gcc
LD=$(CC)
VERSION=1.0
UNAME := $(shell uname -s)
ifeq ($(UNAME), Linux)
HIDAPI_NAME=hidapi-hidraw
else
HIDAPI_NAME=hidapi
endif
ifeq ($(shell uname -o), Msys)
COMPAT_OBJS=sleep.o memmem.o strcasestr.o app.o
PLATFORM_CFLAGS=-DWINDOWS
EXTRA_LDFLAGS=-mwindows # uncomment for console output
MAKENSIS=/c/Program\ Files\ \(x86\)/NSIS/makensis
endif
CFLAGS=-Wall -g `pkg-config $(HIDAPI_NAME) --cflags` --std=c99 $(PLATFORM_CFLAGS)
LDFLAGS=`pkg-config $(HIDAPI_NAME) --libs` -g
GTK_CFLAGS=`pkg-config --cflags gtk+-3.0 gmodule-2.0`
GTK_LDFLAGS=`pkg-config --libs gtk+-3.0 gmodule-2.0`
PREFIX=/usr/local
PROGS=gcn64ctl mempak_ls mempak_format mempak_extract_note mempak_insert_note mempak_rm mempak_convert gcn64ctl_gui
MEMPAKLIB_OBJS=mempak.o mempak_fs.o $(COMPAT_OBJS)
.PHONY : clean install release_windows
all: $(PROGS) gui.xml
gcn64ctl_gui: gcn64ctl_gui.o gcn64ctl_gui_mpkedit.o gcn64.o gcn64lib.o hexdump.o ihex.o mempak_gcn64usb.o $(MEMPAKLIB_OBJS)
$(LD) $^ $(LDFLAGS) $(GTK_LDFLAGS) -o $@ $(EXTRA_LDFLAGS)
gcn64ctl: main.o gcn64.o gcn64lib.o hexdump.o gc2n64_adapter.o ihex.o delay.o mempak_gcn64usb.o $(MEMPAKLIB_OBJS)
$(LD) $^ $(LDFLAGS) -o $@
gcn64ctl_gui.o: gcn64ctl_gui.c gcn64ctl_gui.h
$(CC) $(CFLAGS) $(GTK_CFLAGS) -c $<
app.o: app.rc icon.ico
windres app.rc -o app.o
gui.xml: gcn64cfg.glade
grep -v requires gcn64cfg.glade > gui.xml
gcn64ctl_gui_mpkedit.o: gcn64ctl_gui_mpkedit.c gcn64ctl_gui_mpkedit.h
$(CC) $(CFLAGS) $(GTK_CFLAGS) -c $<
mempak_convert: mempak_convert.o $(MEMPAKLIB_OBJS)
$(LD) $^ $(LDFLAGS) -o $@
mempak_rm: mempak_rm.o $(MEMPAKLIB_OBJS)
$(LD) $^ $(LDFLAGS) -o $@
mempak_insert_note: mempak_insert_note.o $(MEMPAKLIB_OBJS)
$(LD) $^ $(LDFLAGS) -o $@
mempak_extract_note: mempak_extract_note.o $(MEMPAKLIB_OBJS)
$(LD) $^ $(LDFLAGS) -o $@
mempak_ls: mempak_ls.o $(MEMPAKLIB_OBJS)
$(LD) $^ $(LDFLAGS) -o $@
mempak_format: mempak_format.o $(MEMPAKLIB_OBJS)
$(LD) $^ $(LDFLAGS) -o $@
%.o: %.c %.h
$(CC) $(CFLAGS) -c $<
clean:
rm -f *.o *.exe $(PROGS)
install:
@echo "Install not done yet. Sorry"
release_windows:
cp $(addsuffix .exe,$(PROGS)) release_windows
cp gui.xml release_windows
cp project_image.png release_windows
$(MAKENSIS) //DVERSION=$(VERSION) gcn64ctl.nsi
ls installers
@echo "Done"

View File

@ -1 +0,0 @@
1 ICON "icon.ico"

View File

@ -1,43 +0,0 @@
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef LIBDRAGON
#include <libdragon.h>
void _delay_us(unsigned long us)
{
wait_ms(us/1000);
}
void _delay_s(unsigned long s)
{
wait_ms(s*1000);
}
#else
#include <unistd.h>
void _delay_us(unsigned long us)
{
usleep(us);
}
void _delay_s(unsigned long s)
{
sleep(s);
}
#endif

View File

@ -1,2 +0,0 @@
void _delay_us(unsigned long us);
void _delay_s(unsigned long s);

View File

@ -1,863 +0,0 @@
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define _GNU_SOURCE // for memmem
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "gcn64lib.h"
#include "gc2n64_adapter.h"
#include "hexdump.h"
#include "ihex.h"
#include "delay.h"
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#endif
int gc2n64_adapter_echotest(gcn64_hdl_t hdl, int channel, int verbose)
{
unsigned char cmd[30];
unsigned char buf[30];
int i, n;
cmd[0] = 'R';
cmd[1] = 0x00; // echo
for (i=0; i<28; i++) {
cmd[i+2] = 'A'+i;
}
n = gcn64lib_rawSiCommand(hdl, channel, cmd, sizeof(buf), buf, sizeof(buf));
if (n<0) {
return n;
}
if (verbose) {
if ((n != sizeof(buf)) || memcmp(cmd, buf, sizeof(buf))) {
printf("Test failed\n");
printf(" Sent [%d]: ", (int)sizeof(cmd)); printHexBuf(cmd, sizeof(cmd));
printf("Received [%d]: ", n); printHexBuf(buf, n);
return -1;
}
}
return (n!= sizeof(buf)) || memcmp(cmd, buf, sizeof(buf));
}
int gc2n64_adapter_storeCurrentMapping(gcn64_hdl_t hdl, int channel, int dst_slot)
{
int n;
unsigned char cmd[3];
cmd[0] = 'R';
cmd[1] = 0x04; // Save current mapping
cmd[2] = dst_slot;
n = gcn64lib_rawSiCommand(hdl, channel, cmd, sizeof(cmd), cmd, 1);
if (n<0) {
return n;
}
if (n != 1) {
fprintf(stderr, "Communication error while storing mapping\n");
return -1;
}
if (cmd[0] == 0x00) {
return gc2n64_adapter_waitNotBusy(hdl, channel, 0);
}
else {
fprintf(stderr, "storeCurrentMapping: Command NACKed\n");
return -1;
}
}
int gc2n64_adapter_setMapping(gcn64_hdl_t hdl, int channel, struct gc2n64_adapter_mapping *mapping)
{
unsigned char buf[64];
unsigned char mapdata[64];
int i, n;
int maplen, togo, done, chunk;
maplen = mapping->n_pairs * 2;
if (maplen > sizeof(mapdata)) {
fprintf(stderr, "Mapping too large\n");
return -1;
}
for (i=0; i<mapping->n_pairs; i++) {
mapdata[i*2] = mapping->pairs[i].gc;
mapdata[i*2 + 1] = mapping->pairs[i].n64;
}
printf("Map data : ");
printHexBuf(mapdata, maplen);
togo = maplen;
done = 0;
chunk = 0;
while (togo) {
int len;
if (togo > 32) {
len = 32;
} else {
len = togo;
}
buf[0] = 'R';
buf[1] = 0x03; // set mapping
buf[2] = chunk;
memcpy(buf + 3, mapdata + done, len);
done+= len;
// printf("Mapping chunk : ");
// printHexBuf(buf, len + 2);
n = gcn64lib_rawSiCommand(hdl, channel, buf, len + 3, buf, 1);
if (n<0) {
return n;
}
if (n != 1) {
fprintf(stderr, "Communication error setting mapping\n");
return -1;
}
togo -= len;
chunk++;
}
return 0;
}
int gc2n64_adapter_getMapping(gcn64_hdl_t hdl, int channel, int mapping_id, struct gc2n64_adapter_mapping *dst_mapping)
{
unsigned char buf[64];
unsigned char cmd[4];
int n;
int mapping_size;
int togo;
cmd[0] = 'R';
cmd[1] = 0x02; // Get mapping
cmd[2] = mapping_id;
cmd[3] = 0; // chunk 0 (size)
n = gcn64lib_rawSiCommand(hdl, channel, cmd, 4, buf, 1);
if (n<0)
return n;
if (n == 1) {
int i, pos;
mapping_size = buf[0];
// printf("Mapping %d size: %d\n", mapping_id, mapping_size);
togo = mapping_size;
for (pos=0, i=0; pos<mapping_size; i++) {
cmd[0] = 'R';
cmd[1] = 0x02; // Get mapping
cmd[2] = mapping_id;
cmd[3] = i+1; // chunk 1 is first 32 byte block, 2nd is next 32 bytes, etc
// printf("Getting block %d\n", i+1);
n = gcn64lib_rawSiCommand(hdl, channel, cmd, 4, buf + pos, togo > 32 ? 32 : togo);
if (n<0) {
return n;
}
// printf("ret: %d\n", n);
if (n==0)
break;
pos += n;
togo -= n;
}
//printf("Received %d bytes\n", pos);
if (n%2) {
fprintf(stderr, "Error: Odd length mapping received\n");
printHexBuf(buf, pos);
return -1;
}
// TODO : Decode this to dst_mapping
dst_mapping->n_pairs = pos/2;
for (i=0; i<dst_mapping->n_pairs; i++) {
dst_mapping->pairs[i].gc = buf[i*2];
dst_mapping->pairs[i].n64 = buf[i*2+1];
}
}
return 0;
}
const char *gc2n64_adapter_getMappingSlotName(unsigned char id, int default_context)
{
switch (id)
{
case MAPPING_SLOT_BUILTIN_CURRENT:
if (default_context) {
return "[Built-in default]";
} else {
return "[Current mapping]";
}
case MAPPING_SLOT_DPAD_UP: return "[D-Pad UP]";
case MAPPING_SLOT_DPAD_DOWN: return "[D-Pad DOWN]";
case MAPPING_SLOT_DPAD_LEFT: return "[D-Pad LEFT]";
case MAPPING_SLOT_DPAD_RIGHT: return "[D-Pad RIGHT]";
}
return "Invalid ID";
}
const char *gc2n64_adapter_getGCname(unsigned char id)
{
const char *names[] = {
"A","B","Z","Start",
"L","R",
"C-stick up (50% threshold)",
"C-stick down (50% threshold)",
"C-stick left (50% threshold)",
"C-stick right (50% threshold)",
"Dpad-up","Dpad-down","Dpad-left","Dpad-right",
"Joystick left-right axis","Joystick up-down axis",
// Extras
"X","Y",
"Joystick up (50% threshold)", "Joystick down (50% threshold)",
"Joystick left (50% threshold)", "Joystick right (50% threshold)",
"Analogic L slider (50% threshold)",
"Analogic R slider (50% threshold)",
"C-stick left-right axis","C-stick up-down axis",
};
if (id == 0xff)
return "None";
if (id < 0 || id >= ARRAY_SIZE(names)) {
return "Error";
}
return names[id];
}
const char *gc2n64_adapter_getN64name(unsigned char id)
{
const char *names[] = {
"A","B","Z","Start","L","R",
"C-up","C-down","C-left","C-right",
"Dpad-up","Dpad-down","Dpad-left","Dpad-right",
"Joystick left-right axis","Joystick up-down axis",
"Joystick up", "Joystick down",
"Joystick left", "Joystick right",
"None"
};
if (id == 0xff)
return "None";
if (id < 0 || id >= ARRAY_SIZE(names)) {
return "Error";
}
return names[id];
}
struct gc2n64_adapter_mapping *gc2n64_adapter_loadMapping(const char *srcfile)
{
FILE *fptr;
struct gc2n64_adapter_mapping *map = NULL;;
char linebuf[64];
int line = 0, pair = 0;
fptr = fopen(srcfile, "r");
if (!fptr) {
perror("fopen");
return NULL;
}
map = malloc(sizeof(struct gc2n64_adapter_mapping));
if (!map) {
perror("malloc");
goto err;
}
do {
if (fgets(linebuf, sizeof(linebuf), fptr)) {
int gc, n64, n;
line++;
if (line == 1) {
const char *magic = "# gc2n64 mapping";
if (strncmp(magic, linebuf, strlen(magic))) {
fprintf(stderr, "Does not appear to be a valid mapping file\n");
goto err;
}
continue;
}
n = sscanf(linebuf, "%03d;%03d", &gc, &n64);
if (n != 2) {
// printf("Ignoring line %d\n", line);
} else {
// printf("%d -> %d\n", gc, n64);
map->pairs[pair].gc = gc;
map->pairs[pair].n64 = n64;
pair++;
if (pair >= GC2N64_MAX_MAPPING_PAIRS) {
fprintf(stderr, "too many pairs, cannot load mapping.\n");
goto err;
}
}
}
} while (!feof(fptr));
map->n_pairs = pair;
fclose(fptr);
return map;
err:
if (map) {
free(map);
}
fclose(fptr);
return NULL;
}
int gc2n64_adapter_saveMapping(struct gc2n64_adapter_mapping *map, const char *dstfile)
{
FILE *fptr;
int i;
fptr = fopen(dstfile, "w");
if (!fptr) {
perror("fopen");
return -1;
}
fprintf(fptr, "# gc2n64 mapping\n");
for (i=0; i<map->n_pairs; i++) {
fprintf(fptr, "%03d;%03d # %s -> %s\n",
map->pairs[i].gc, map->pairs[i].n64,
gc2n64_adapter_getGCname(map->pairs[i].gc),
gc2n64_adapter_getN64name(map->pairs[i].n64));
}
fflush(fptr);
fclose(fptr);
return 0;
}
void gc2n64_adapter_printMapping(struct gc2n64_adapter_mapping *map)
{
int i;
int is_default;
for (i=0; i<map->n_pairs; i++) {
// Do not display the terminator
if (map->pairs[i].gc == 0xff || map->pairs[i].n64 == 0xff) {
break;
}
/* 0 .. 15 is a 1:1 (same button name) mapping by default */
if (map->pairs[i].gc < 16) {
if (map->pairs[i].gc == map->pairs[i].n64) {
is_default = 1;
}
else {
is_default = 0;
}
}
else {
// 16 and above maps to NONE by default
if (map->pairs[i].n64 == 20) {
is_default = 1;
} else {
is_default = 0;
}
}
if (!is_default) {
printf("%s -> %s, ", gc2n64_adapter_getGCname(map->pairs[i].gc),
gc2n64_adapter_getN64name(map->pairs[i].n64));
}
}
}
void gc2n64_adapter_printInfo(struct gc2n64_adapter_info *inf)
{
int i;
if (!inf->in_bootloader) {
printf("gc_to_n64 adapter info: {\n");
printf("\tDefault mapping id: %d (%s)\n", inf->app.default_mapping_id, gc2n64_adapter_getMappingSlotName(inf->app.default_mapping_id, 1) );
printf("\tDeadzone enabled: %d\n", inf->app.deadzone_enabled);
printf("\tOld v1.5 conversion: %d\n", inf->app.old_v1_5_conversion);
printf("\tFirmware version: %s\n", inf->app.version);
printf("\tUpgradable: %s\n", inf->app.upgradeable ? "Yes":"No (Atmega8)");
for (i=0; i<GC2N64_NUM_MAPPINGS; i++) {
printf("\tMapping %d (%-13s): { ", i, gc2n64_adapter_getMappingSlotName(i, 0));
gc2n64_adapter_printMapping(&inf->app.mappings[i]);
printf(" }\n");
}
} else {
printf("gc_to_n64 adapter in bootloader mode: {\n");
printf("\tBootloader firmware version: %s\n", inf->bootldr.version);
printf("\tMCU page size: %d bytes\n", inf->bootldr.mcu_page_size);
printf("\tBootloader code start address: 0x%04x\n", inf->bootldr.bootloader_start_address);
}
printf("}\n");
}
int gc2n64_adapter_getInfo(gcn64_hdl_t hdl, int channel, struct gc2n64_adapter_info *inf)
{
unsigned char buf[32];
int n;
buf[0] = 'R';
buf[1] = 0x01; // Get device info
n = gcn64lib_rawSiCommand(hdl, channel, buf, 2, buf, sizeof(buf));
if (n<0)
return n;
if (n > 0) {
// On N64, when receiving an all 0xFF reply, catch it here.
if (buf[0] == 0xff)
return -1;
if (!inf)
return 0;
inf->in_bootloader = buf[0];
if (!inf->in_bootloader) {
inf->app.default_mapping_id = buf[1];
inf->app.deadzone_enabled = buf[2];
inf->app.old_v1_5_conversion = buf[3];
inf->app.upgradeable = buf[9];
inf->app.version[sizeof(inf->app.version)-1]=0;
strncpy(inf->app.version, (char*)buf+10, sizeof(inf->app.version)-1);
} else {
inf->bootldr.mcu_page_size = buf[1];
inf->bootldr.bootloader_start_address = buf[2] << 8 | buf[3];
inf->bootldr.version[sizeof(inf->bootldr.version)-1]=0;
strncpy(inf->bootldr.version, (char*)buf+10, sizeof(inf->bootldr.version)-1);
}
for (n=0; n<GC2N64_NUM_MAPPINGS; n++) {
gc2n64_adapter_getMapping(hdl, channel, n, &inf->app.mappings[n]);
}
} else {
printf("No answer (old version?)\n");
return -1;
}
return 0;
}
int gc2n64_adapter_isBusy(gcn64_hdl_t hdl, int channel)
{
unsigned char buf[64];
int n;
buf[0] = 'R';
buf[1] = 0xf9;
n = gcn64lib_rawSiCommand(hdl, channel, buf, 2, buf, 1);
if (n<0)
return n;
if (n != 1) {
return 2; // Busy inferred from lack of answer
}
if (buf[0] != 0x00) {
return 1; // Busy
}
return 0; // Idle
}
int gc2n64_adapter_waitNotBusy(gcn64_hdl_t hdl, int channel, int verbose)
{
char spinner[4] = { '|','/','-','\\' };
int busy, no_reply_count=0;
int c=0;
while ((busy = gc2n64_adapter_isBusy(hdl, channel)))
{
if (busy < 0) {
return -1;
}
if (busy == 2) {
no_reply_count++;
if (no_reply_count > 200) {
fprintf(stderr, "Adapter answer timeout\n");
return -1;
}
}
printf("%c\b", spinner[c%4]); fflush(stdout);
c++;
_delay_us(50000);
}
return 0;
}
int gc2n64_adapter_boot_eraseAll(gcn64_hdl_t hdl, int channel)
{
unsigned char buf[64];
int n;
buf[0] = 'R';
buf[1] = 0xf0;
n = gcn64lib_rawSiCommand(hdl, channel, buf, 2, buf, 1);
if (n<0)
return n;
if (n != 1) {
fprintf(stderr, "Invalid answer. %d bytes received.\n", n);
return -1;
}
if (buf[0] != 0x00) {
fprintf(stderr, "eraseAll request NACK!\n");
return -1;
}
return 0;
}
int gc2n64_adapter_boot_readBlock(gcn64_hdl_t hdl, int channel, unsigned int block_id, unsigned char dst[32])
{
unsigned char buf[32];
int n;
buf[0] = 'R';
buf[1] = 0xf1;
buf[2] = block_id >> 8;
buf[3] = block_id & 0xff;
n = gcn64lib_rawSiCommand(hdl, channel, buf, 4, buf, sizeof(buf));
if (n<0)
return n;
if (n != 32) {
fprintf(stderr, "Invalid answer\n");
return -1;
}
memcpy(dst, buf, 32);
return 0;
}
int gc2n64_adapter_dumpFlash(gcn64_hdl_t hdl, int channel)
{
int i;
unsigned char buf[0x10000];
struct gc2n64_adapter_info inf;
i = gc2n64_adapter_getInfo(hdl, channel, &inf);
if (i)
return i;
if (!inf.in_bootloader) {
fprintf(stderr, "dumpFlash: Nnot in bootloader\n");
return -1;
}
// Atmega168 : 16K
for (i=0; i<16*1024; i+= 32)
{
gc2n64_adapter_boot_readBlock(hdl, channel, i/32, buf + i);
printf("0x%04x: ", i);
printHexBuf(buf + i, 32);
}
return 0;
}
int gc2n64_adapter_enterBootloader(gcn64_hdl_t hdl, int channel)
{
unsigned char buf[4];
int n;
int t = 1000; // > 100ms timeout
/* The bootloader starts the application automatically if it is
* installed. To prevent the application from being restarted right
* away when are entering the bootloader, the bootloader waits
* 50 ms at startup, and if it receives the 'enter bootloader' command
* within this window, the application is not started.
*
* Also, contrary to the application, the bootloader actually answers
* this command. So it doubles as a handshake to know the bootloader has
* started and is ready to receive instructions.
*
* */
do {
buf[0] = 'R';
buf[1] = 0xff;
n = gcn64lib_rawSiCommand(hdl, channel, buf, 2, buf, sizeof(buf));
if (n<0) {
return n;
}
if (buf[0] == 0xff && buf[1] == 0xff) {
n = 0;
}
_delay_us(1000);
t--;
if (!t) {
fprintf(stderr, "Timeout waiting for bootloader\n");
return -1;
}
}
while(n==0);
return 0;
}
int gc2n64_adapter_bootApplication(gcn64_hdl_t hdl, int channel)
{
unsigned char buf[2];
int n;
buf[0] = 'R';
buf[1] = 0xfe;
n = gcn64lib_rawSiCommand(hdl, channel, buf, 2, buf, 1);
if (n<0)
return n;
if (n != 1) {
fprintf(stderr, "boot application: Invalid answer\n");
return -1;
}
if (buf[0]) {
fprintf(stderr, "Boot nack\n");
return -1;
}
return 0;
}
// Note: eraseAll needs to be performed first
int gc2n64_adapter_sendFirmwareBlocks(gcn64_hdl_t hdl, int channel, unsigned char *firmware, int len)
{
unsigned char buf[64];
int i, block_id;
int n;
for (i=0; i<len; i+=32) {
block_id = i / 32;
buf[0] = 'R';
buf[1] = 0xf2;
buf[2] = block_id >> 8;
buf[3] = block_id & 0xff;
memcpy(buf + 4, firmware+i, 32);
printf("Block %d / %d\r", block_id+1, len / 32); fflush(stdout);
n = gcn64lib_rawSiCommand(hdl, channel, buf, 4 + 32, buf, 4);
if (n<0) {
fprintf(stderr, "\nRaw command failed\n");
return n;
}
if (n != 4) {
fprintf(stderr, "\nInvalid upload block answer\n");
return -1;
}
// [0] ACK (should be 0x00)
// [1] Need to poll?
// [2] Block ID high
// [3] Block ID low
if (buf[0] != 0x00) {
fprintf(stderr, "Busy\n");
return -1;
}
if (buf[1]) {
if (gc2n64_adapter_waitNotBusy(hdl, channel, 1)) {
fprintf(stderr, "Error waiting not busy\n");
return -1;
}
}
// printf("\n");
// printf("Block ID: 0x%04x\n", (buf[2]<<8) | buf[3]);
}
return 0;
}
int gc2n64_adapter_verifyFirmware(gcn64_hdl_t hdl, int channel, unsigned char *firmware, int len)
{
unsigned char buf[32];
int i;
for (i=0; i<len; i+=32) {
gc2n64_adapter_boot_readBlock(hdl, channel, i/32, buf);
if (memcmp(buf, firmware + i, 32)) {
printf("\nMismatch in block address 0x%04x\n", i);
printf("Written: "); printHexBuf(firmware + i, 32);
printf(" Read: "); printHexBuf(buf, 32);
return -1;
} else {
printf("Block %d / %d ok\r", i/32 + 1, len / 32); fflush(stdout);
}
}
return 0;
}
int gc2n64_adapter_waitForBootloader(gcn64_hdl_t hdl, int channel, int timeout_s)
{
struct gc2n64_adapter_info inf;
int i;
int n;
for (i=0; i<=timeout_s; i++) {
n = gc2n64_adapter_getInfo(hdl, channel, &inf);
// Errors (caused by timeouts) are just ignored since they are expected.
if (n == 0) {
gc2n64_adapter_printInfo(&inf);
if (inf.in_bootloader)
return 0;
}
_delay_s(1);
}
return -1;
}
#define FIRMWARE_BUF_SIZE 0x10000
int gc2n64_adapter_updateFirmware(gcn64_hdl_t hdl, int channel, const char *hexfile)
{
unsigned char *buf;
int max_addr;
int ret = 0, res;
struct gc2n64_adapter_info inf;
const char *signature = "41d938a8-6f8a-11e5-a45e-001bfca3c593";
////////////////////
printf("step [1/7] : Load .hex file...\n");
buf = malloc(FIRMWARE_BUF_SIZE);
if (!buf) {
perror("malloc");
return -1;
}
memset(buf, 0xff, FIRMWARE_BUF_SIZE);
max_addr = load_ihex(hexfile, buf, FIRMWARE_BUF_SIZE);
if (max_addr < 0) {
fprintf(stderr, "Update failed : Could not load hex file\n");
ret = -1;
goto err;
}
// look for the signature somewhere in the file to make sure
// this firmware is intended for this product
if (!memmem(buf, max_addr + 1, signature, strlen(signature))) {
fprintf(stderr, "Update aborted : Signature not found. This hex file is not for this adapter.\n");
ret = -1;
printHexBuf(buf + 0x1bde, 30);
goto err;
}
printf("Firmware size: %d bytes\n", max_addr+1);
////////////////////
printf("step [2/7] : Get adapter info...\n");
res = gc2n64_adapter_getInfo(hdl, channel, &inf);
if (res < 0) {
fprintf(stderr, "Failed to read adapter info\n");
return -1;
}
gc2n64_adapter_printInfo(&inf);
if (inf.in_bootloader) {
printf("step [3/7] : Enter bootloader... Skipped. Already in bootloader.\n");
} else {
// Catch Atmega8 adapters programmed with a new firmware but without bootloader.
if (!inf.app.upgradeable) {
fprintf(stderr, "Error : This adapter is not upgradable. (i.e. No bootloader on Atmega8)\n");
ret = -1;
goto err;
}
printf("step [3/7] : Enter bootloader...\n");
res = gc2n64_adapter_enterBootloader(hdl, channel);
if (res < 0) {
fprintf(stderr, "Failed to enter the bootloader\n");
ret = -1;
goto err;
}
// Re-read the info structure, as we will need the bootloader start address.
res = gc2n64_adapter_getInfo(hdl, channel, &inf);
if (res < 0) {
fprintf(stderr, "Failed to read info after enterring bootloader\n");
ret = -1;
goto err;
}
}
////////////////////
printf("step [4/7] : Erase current firmware... "); fflush(stdout);
gc2n64_adapter_boot_eraseAll(hdl, channel);
if (gc2n64_adapter_waitNotBusy(hdl, channel, 1)) {
ret = -1;
goto err;
}
printf("Ok\n");
printf("step [5/7] : Write new firmware...\n");
// Note: We write up to the bootloader, even if the firmware was shorter (it usually is).
// This is to make sure that the marker we placed at the end gets written.
res = gc2n64_adapter_sendFirmwareBlocks(hdl, channel, buf, inf.bootldr.bootloader_start_address);
if (res < 0) {
ret = -1;
goto err;
}
printf("step [6/7] : Verify firmware...\n");
res = gc2n64_adapter_verifyFirmware(hdl, channel, buf, inf.bootldr.bootloader_start_address);
if (res < 0) {
printf("Verify failed : Update failed\n");
ret = -1;
goto err;
}
printf("step [7/7] : Launch new firmware.\n");
gc2n64_adapter_bootApplication(hdl, channel);
err:
free(buf);
return ret;
}

View File

@ -1,74 +0,0 @@
#ifndef _gc2n64_adapter_h__
#define _gc2n64_adapter_h__
#include "gcn64.h"
#define GC2N64_MAX_MAPPING_PAIRS 32
#define GC2N64_NUM_MAPPINGS 5
struct gc2n64_adapter_mapping_pair {
int gc, n64;
};
struct gc2n64_adapter_mapping {
int n_pairs;
struct gc2n64_adapter_mapping_pair pairs[GC2N64_MAX_MAPPING_PAIRS];
};
struct gc2n64_adapter_info_app {
unsigned char default_mapping_id;
unsigned char deadzone_enabled;
unsigned char old_v1_5_conversion;
unsigned char upgradeable;
char version[16];
struct gc2n64_adapter_mapping mappings[GC2N64_NUM_MAPPINGS];
};
struct gc2n64_adapter_info_bootloader {
char version[16];
unsigned char mcu_page_size;
unsigned short bootloader_start_address;
};
struct gc2n64_adapter_info {
int in_bootloader;
union {
struct gc2n64_adapter_info_app app;
struct gc2n64_adapter_info_bootloader bootldr;
};
};
int gc2n64_adapter_echotest(gcn64_hdl_t hdl, int channel, int verbosee);
int gc2n64_adapter_getInfo(gcn64_hdl_t hdl, int channel, struct gc2n64_adapter_info *inf);
void gc2n64_adapter_printInfo(struct gc2n64_adapter_info *inf);
void gc2n64_adapter_printMapping(struct gc2n64_adapter_mapping *map);
#define MAPPING_SLOT_BUILTIN_CURRENT 0
#define MAPPING_SLOT_DPAD_UP 1
#define MAPPING_SLOT_DPAD_DOWN 2
#define MAPPING_SLOT_DPAD_LEFT 3
#define MAPPING_SLOT_DPAD_RIGHT 4
const char *gc2n64_adapter_getMappingSlotName(unsigned char id, int default_context);
int gc2n64_adapter_getMapping(gcn64_hdl_t hdl, int channel, int mapping_id, struct gc2n64_adapter_mapping *dst_mapping);
int gc2n64_adapter_setMapping(gcn64_hdl_t hdl, int channel, struct gc2n64_adapter_mapping *mapping);
int gc2n64_adapter_storeCurrentMapping(gcn64_hdl_t hdl, int channel, int dst_slot);
int gc2n64_adapter_saveMapping(struct gc2n64_adapter_mapping *map, const char *dstfile);
struct gc2n64_adapter_mapping *gc2n64_adapter_loadMapping(const char *srcfile);
int gc2n64_adapter_waitNotBusy(gcn64_hdl_t hdl, int channel, int verbose);
int gc2n64_adapter_boot_isBusy(gcn64_hdl_t hdl, int channel);
int gc2n64_adapter_boot_eraseAll(gcn64_hdl_t hdl, int channel);
int gc2n64_adapter_boot_readBlock(gcn64_hdl_t hdl, int channel, unsigned int block_id, unsigned char dst[32]);
int gc2n64_adapter_dumpFlash(gcn64_hdl_t hdl, int channel);
int gc2n64_adapter_updateFirmware(gcn64_hdl_t hdl, int channel, const char *hexfile);
int gc2n64_adapter_enterBootloader(gcn64_hdl_t hdl, int channel);
int gc2n64_adapter_bootApplication(gcn64_hdl_t hdl, int channel);
int gc2n64_adapter_sendFirmwareBlocks(gcn64_hdl_t hdl, int channel, unsigned char *firmware, int len);
int gc2n64_adapter_verifyFirmware(gcn64_hdl_t hdl, int channel, unsigned char *firmware, int len);
int gc2n64_adapter_waitForBootloader(gcn64_hdl_t hdl, int channel, int timeout_s);
#endif // _gc2n64_adapter_h__

View File

@ -1,287 +0,0 @@
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "gcn64.h"
#include "gcn64_priv.h"
#include "../requests.h"
#include "hidapi.h"
static int dusbr_verbose = 0;
#define GCN64_HID_DATA_REPORT_SIZE 40
#define IS_VERBOSE() (dusbr_verbose)
int gcn64_init(int verbose)
{
dusbr_verbose = verbose;
hid_init();
return 0;
}
void gcn64_shutdown(void)
{
hid_exit();
}
static char isProductIdHandled(unsigned short pid, int interface_number)
{
switch (pid)
{
case 0x0017: // GC/N64 USB v3
if (interface_number == 1)
return 1;
break;
}
return 0;
}
struct gcn64_list_ctx *gcn64_allocListCtx(void)
{
struct gcn64_list_ctx *ctx;
ctx = calloc(1, sizeof(struct gcn64_list_ctx));
return ctx;
}
void gcn64_freeListCtx(struct gcn64_list_ctx *ctx)
{
if (ctx) {
if (ctx->devs) {
hid_free_enumeration(ctx->devs);
}
free(ctx);
}
}
/**
* \brief List instances of our rgbleds device on the USB busses.
* \param info Pointer to gcn64_info structure to store data
* \param dst Destination buffer for device serial number/id.
* \param dstbuf_size Destination buffer size.
*/
struct gcn64_info *gcn64_listDevices(struct gcn64_info *info, struct gcn64_list_ctx *ctx)
{
memset(info, 0, sizeof(struct gcn64_info));
if (!ctx) {
fprintf(stderr, "gcn64_listDevices: Passed null context\n");
return NULL;
}
if (ctx->devs)
goto jumpin;
if (IS_VERBOSE()) {
printf("Start listing\n");
}
ctx->devs = hid_enumerate(OUR_VENDOR_ID, 0x0000);
if (!ctx->devs) {
printf("Hid enumerate returned NULL\n");
return NULL;
}
for (ctx->cur_dev = ctx->devs; ctx->cur_dev; ctx->cur_dev = ctx->cur_dev->next)
{
if (IS_VERBOSE()) {
printf("Considering 0x%04x:0x%04x\n", ctx->cur_dev->vendor_id, ctx->cur_dev->product_id);
}
if (isProductIdHandled(ctx->cur_dev->product_id, ctx->cur_dev->interface_number))
{
info->usb_vid = ctx->cur_dev->vendor_id;
info->usb_pid = ctx->cur_dev->product_id;
wcsncpy(info->str_prodname, ctx->cur_dev->product_string, PRODNAME_MAXCHARS-1);
wcsncpy(info->str_serial, ctx->cur_dev->serial_number, SERIAL_MAXCHARS-1);
strncpy(info->str_path, ctx->cur_dev->path, PATH_MAXCHARS-1);
return info;
}
jumpin:
// prevent 'error: label at end of compound statement'
continue;
}
return NULL;
}
gcn64_hdl_t gcn64_openDevice(struct gcn64_info *dev)
{
hid_device *hdev;
if (!dev)
return NULL;
if (IS_VERBOSE()) {
printf("Opening device path: '%s'\n", dev->str_path);
}
hdev = hid_open_path(dev->str_path);
if (!hdev) {
return NULL;
}
return hdev;
}
gcn64_hdl_t gcn64_openBy(struct gcn64_info *dev, unsigned char flags)
{
struct gcn64_list_ctx *ctx;
struct gcn64_info inf;
gcn64_hdl_t h;
if (IS_VERBOSE())
printf("gcn64_openBy, flags=0x%02x\n", flags);
ctx = gcn64_allocListCtx();
if (!ctx)
return NULL;
while (gcn64_listDevices(&inf, ctx)) {
if (IS_VERBOSE())
printf("Considering '%s'\n", inf.str_path);
if (flags & GCN64_FLG_OPEN_BY_SERIAL) {
if (wcscmp(inf.str_serial, dev->str_serial))
continue;
}
if (flags & GCN64_FLG_OPEN_BY_PATH) {
if (strcmp(inf.str_path, dev->str_path))
continue;
}
if (flags & GCN64_FLG_OPEN_BY_VID) {
if (inf.usb_vid != dev->usb_vid)
continue;
}
if (flags & GCN64_FLG_OPEN_BY_PID) {
if (inf.usb_pid != dev->usb_pid)
continue;
}
if (IS_VERBOSE())
printf("Found device. opening...\n");
h = gcn64_openDevice(&inf);
gcn64_freeListCtx(ctx);
return h;
}
gcn64_freeListCtx(ctx);
return NULL;
}
void gcn64_closeDevice(gcn64_hdl_t hdl)
{
hid_device *hdev = (hid_device*)hdl;
if (hdev) {
hid_close(hdev);
}
}
int gcn64_send_cmd(gcn64_hdl_t hdl, const unsigned char *cmd, int cmdlen)
{
hid_device *hdev = (hid_device*)hdl;
unsigned char buffer[GCN64_HID_DATA_REPORT_SIZE+1];
int n;
if (cmdlen > (sizeof(buffer)-1)) {
fprintf(stderr, "Error: Command too long\n");
return -1;
}
memset(buffer, 0, sizeof(buffer));
buffer[0] = 0x00; // report ID set to 0 (device has only one)
memcpy(buffer + 1, cmd, cmdlen);
n = hid_send_feature_report(hdev, buffer, sizeof(buffer));
if (n < 0) {
fprintf(stderr, "Could not send feature report (%ls)\n", hid_error(hdev));
return -1;
}
return 0;
}
int gcn64_poll_result(gcn64_hdl_t hdl, unsigned char *cmd, int cmd_maxlen)
{
hid_device *hdev = (hid_device*)hdl;
unsigned char buffer[GCN64_HID_DATA_REPORT_SIZE+1];
int res_len;
int n;
memset(buffer, 0, sizeof(buffer));
buffer[0] = 0x00; // report ID set to 0 (device has only one)
n = hid_get_feature_report(hdev, buffer, sizeof(buffer));
if (n < 0) {
fprintf(stderr, "Could not send feature report (%ls)\n", hid_error(hdev));
return -1;
}
if (n==0) {
return 0;
}
res_len = n-1;
if (res_len>0) {
int copy_len;
copy_len = res_len;
if (copy_len > cmd_maxlen) {
copy_len = cmd_maxlen;
}
if (cmd) {
memcpy(cmd, buffer+1, copy_len);
}
}
return res_len;
}
int gcn64_exchange(gcn64_hdl_t hdl, unsigned char *outcmd, int outlen, unsigned char *result, int result_max)
{
int n;
n = gcn64_send_cmd(hdl, outcmd, outlen);
if (n<0) {
fprintf(stderr, "Error sending command\n");
return -1;
}
/* Answer to the command comes later. For now, this is polled, but in
* the future an interrupt-in transfer could be used. */
do {
n = gcn64_poll_result(hdl, result, result_max);
if (n < 0) {
fprintf(stderr, "Error\r\n");
break;
}
if (n==0) {
// printf("."); fflush(stdout);
}
} while (n==0);
return n;
}

View File

@ -1,53 +0,0 @@
#ifndef _gcn64_h__
#define _gcn64_h__
#include <wchar.h>
#define OUR_VENDOR_ID 0x289b
#define PRODNAME_MAXCHARS 256
#define SERIAL_MAXCHARS 256
#define PATH_MAXCHARS 256
struct gcn64_info {
wchar_t str_prodname[PRODNAME_MAXCHARS];
wchar_t str_serial[SERIAL_MAXCHARS];
char str_path[PATH_MAXCHARS];
int usb_vid, usb_pid;
int major, minor;
int access; // True unless direct access to read serial/prodname failed due to permissions.
};
struct gcn64_list_ctx;
typedef void* gcn64_hdl_t; // Cast from hid_device
int gcn64_init(int verbose);
void gcn64_shutdown(void);
struct gcn64_list_ctx *gcn64_allocListCtx(void);
void gcn64_freeListCtx(struct gcn64_list_ctx *ctx);
struct gcn64_info *gcn64_listDevices(struct gcn64_info *info, struct gcn64_list_ctx *ctx);
gcn64_hdl_t gcn64_openDevice(struct gcn64_info *dev);
#define GCN64_FLG_OPEN_BY_SERIAL 1 /** Serial must match */
#define GCN64_FLG_OPEN_BY_PATH 2 /** Path must match */
#define GCN64_FLG_OPEN_BY_VID 4 /** USB VID must match */
#define GCN64_FLG_OPEN_BY_PID 8 /** USB PID MUST match */
/**
* \brief Open a device matching a serial number
* \param dev The device structure
* \param flags Flags
* \return A handle to the opened device, or NULL if not found
**/
gcn64_hdl_t gcn64_openBy(struct gcn64_info *dev, unsigned char flags);
void gcn64_closeDevice(gcn64_hdl_t hdl);
int gcn64_send_cmd(gcn64_hdl_t hdl, const unsigned char *cmd, int len);
int gcn64_poll_result(gcn64_hdl_t hdl, unsigned char *cmd, int cmdlen);
int gcn64_exchange(gcn64_hdl_t hdl, unsigned char *outcmd, int outlen, unsigned char *result, int result_max);
#endif // _gcn64_h__

View File

@ -1,10 +0,0 @@
#ifndef _gcn64_priv_h__
#define _gcn64_priv_h__
#include "hidapi.h"
struct gcn64_list_ctx {
struct hid_device_info *devs, *cur_dev;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,97 +0,0 @@
; example2.nsi
;
; This script is based on example1.nsi, but it remember the directory,
; has uninstall support and (optionally) installs start menu shortcuts.
;
; It will install example2.nsi into a directory that the user selects,
;--------------------------------
; The name of the installer
Name "gcn64ctl"
; The file to write
OutFile "installers/gcn64ctl-install-${VERSION}.exe"
; The default installation directory
InstallDir $PROGRAMFILES\gcn64ctl
; Registry key to check for directory (so if you install again, it will
; overwrite the old one automatically)
InstallDirRegKey HKLM "Software\gcn64ctl" "Install_Dir"
LicenseData ../LICENSE
; Request application privileges for Windows Vista
RequestExecutionLevel admin
;--------------------------------
!include "LogicLib.nsh"
!include "x64.nsh"
; Pages
Page license
Page components
Page directory
Page instfiles
UninstPage uninstConfirm
UninstPage instfiles
;--------------------------------
; The stuff to install
Section "gcn64ctl (required)"
SectionIn RO
; Set output path to the installation directory.
SetOutPath $INSTDIR
; Put file there
File /r "release_windows\*.*"
; Write the installation path into the registry
WriteRegStr HKLM SOFTWARE\gcn64ctl "Install_Dir" "$INSTDIR"
; Write the uninstall keys for Windows
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\gcn64ctl" "DisplayName" "gcn64ctl"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\gcn64ctl" "UninstallString" '"$INSTDIR\uninstall.exe"'
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\gcn64ctl" "NoModify" 1
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\gcn64ctl" "NoRepair" 1
WriteUninstaller "uninstall.exe"
SectionEnd
; Optional section (can be disabled by the user)
Section "Start Menu Shortcuts"
CreateDirectory "$SMPROGRAMS\raphnet-tech GC/N64 adapter manager"
CreateShortCut "$SMPROGRAMS\raphnet-tech GC/N64 adapter manager\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0
CreateShortCut "$SMPROGRAMS\raphnet-tech GC/N64 adapter manager\Commandi-line tools.lnk" "$SYSDIR\cmd.exe" '/K "cd /d $INSTDIR"' "$SYSDIR\cmd.exe" 0
CreateShortCut "$SMPROGRAMS\raphnet-tech GC/N64 adapter manager\GC/N64 adapter manager.lnk" "$INSTDIR\gcn64ctl_gui.exe" "" "$INSTDIR\gcn64ctl_gui.exe" 0
SectionEnd
;--------------------------------
; Uninstaller
Section "Uninstall"
; Remove registry keys
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\gcn64ctl"
DeleteRegKey HKLM SOFTWARE\gcn64ctl
; Remove files and uninstaller
Delete "$INSTDIR\*.*"
; Remove shortcuts, if any
Delete "$SMPROGRAMS\raphnet-tech GC/N64 adapter manager\*.*"
; Remove directories used
RMDir "$SMPROGRAMS\gcn64ctl"
RMDir "$INSTDIR"
SectionEnd

View File

@ -1,845 +0,0 @@
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../requests.h"
#include "ihex.h"
#include "mempak.h"
#include "gcn64ctl_gui.h"
#include "mempak_gcn64usb.h"
#include "gcn64lib.h"
static void updateGuiFromAdapter(struct application *app);
gboolean rebuild_device_list_store(gpointer data);
void deselect_adapter(struct application *app);
gboolean updateDonefunc(gpointer data)
{
struct application *app = data;
GET_UI_ELEMENT(GtkDialog, firmware_update_dialog);
printf("updateDonefunc\n");
gtk_dialog_response(firmware_update_dialog, app->update_dialog_response);
g_thread_join(app->updater_thread);
rebuild_device_list_store(data);
updateGuiFromAdapter(app);
app->inhibit_periodic_updates = 0;
return FALSE;
}
gboolean updateProgress(gpointer data)
{
struct application *app = data;
GET_UI_ELEMENT(GtkProgressBar, updateProgress);
GET_UI_ELEMENT(GtkLabel, updateStatus);
gtk_progress_bar_set_fraction(updateProgress, app->update_percent / 100.0);
gtk_label_set_text(updateStatus, app->update_status);
return FALSE;
}
gboolean closeAdapter(gpointer data)
{
struct application *app = data;
deselect_adapter(app);
return FALSE;
}
gpointer updateThreadFunc(gpointer data)
{
struct application *app = data;
int res;
FILE *dfu_fp;
char linebuf[256];
char cmd[256];
int retries = 10;
app->inhibit_periodic_updates = 1;
app->update_status = "Starting...";
app->update_percent = 1;
gdk_threads_add_idle(updateProgress, data);
app->update_percent = 10;
app->update_status = "Enter bootloader...";
gcn64lib_bootloader(app->current_adapter_handle);
gdk_threads_add_idle(closeAdapter, data);
app->update_percent = 19;
app->update_status = "Erasing chip...";
do {
app->update_percent++;
gdk_threads_add_idle(updateProgress, data);
if (app->at90usb1287) {
dfu_fp = popen("dfu-programmer at90usb1287 erase", "r");
} else {
dfu_fp = popen("dfu-programmer atmega32u2 erase", "r");
}
if (!dfu_fp) {
app->update_dialog_response = GTK_RESPONSE_REJECT;
gdk_threads_add_idle(updateDonefunc, data);
return NULL;
}
do {
fgets(linebuf, sizeof(linebuf), dfu_fp);
} while(!feof(dfu_fp));
res = pclose(dfu_fp);
printf("Pclose: %d\n", res);
if (res==0) {
break;
}
sleep(1);
} while (retries--);
if (!retries) {
app->update_dialog_response = GTK_RESPONSE_REJECT;
gdk_threads_add_idle(updateDonefunc, data);
}
app->update_status = "Chip erased";
app->update_percent = 20;
gdk_threads_add_idle(updateProgress, data);
app->update_status = "Programming ...";
app->update_percent = 30;
gdk_threads_add_idle(updateProgress, data);
snprintf(cmd, sizeof(cmd), "dfu-programmer %s flash %s",
app->at90usb1287 ? "at90usb1287" : "atmega32u2",
app->updateHexFile);
dfu_fp = popen(cmd, "r");
if (!dfu_fp) {
app->update_dialog_response = GTK_RESPONSE_REJECT;
gdk_threads_add_idle(updateDonefunc, data);
return NULL;
}
do {
fgets(linebuf, sizeof(linebuf), dfu_fp);
printf("ln: %s\n\n", linebuf);
if (strstr(linebuf, "Validating")) {
app->update_status = "Validating...";
app->update_percent = 70;
gdk_threads_add_idle(updateDonefunc, data);
}
} while(!feof(dfu_fp));
res = pclose(dfu_fp);
printf("Pclose: %d\n", res);
if (res != 0) {
app->update_dialog_response = GTK_RESPONSE_REJECT;
gdk_threads_add_idle(updateDonefunc, data);
return NULL;
}
app->update_status = "Starting firmware...";
app->update_percent = 80;
gdk_threads_add_idle(updateProgress, data);
dfu_fp = popen("dfu-programmer at90usb1287 start", "r");
if (!dfu_fp) {
app->update_dialog_response = GTK_RESPONSE_REJECT;
gdk_threads_add_idle(updateDonefunc, data);
return NULL;
}
res = pclose(dfu_fp);
printf("Pclose: %d\n", res);
if (res!=0) {
app->update_dialog_response = GTK_RESPONSE_REJECT;
gdk_threads_add_idle(updateDonefunc, data);
return NULL;
}
app->update_status = "Waiting for device...";
app->update_percent = 90;
gdk_threads_add_idle(updateProgress, data);
retries = 6;
do {
app->current_adapter_handle = gcn64_openBy(&app->current_adapter_info, GCN64_FLG_OPEN_BY_SERIAL);
if (app->current_adapter_handle)
break;
sleep(1);
app->update_percent++;
gdk_threads_add_idle(updateProgress, data);
} while (retries--);
if (!app->current_adapter_handle) {
app->update_dialog_response = GTK_RESPONSE_REJECT;
gdk_threads_add_idle(updateDonefunc, data);
return NULL;
}
app->update_status = "Done";
app->update_percent = 100;
gdk_threads_add_idle(updateProgress, data);
printf("Update done\n");
app->update_dialog_response = GTK_RESPONSE_OK;
gdk_threads_add_idle(updateDonefunc, data);
return NULL;
}
G_MODULE_EXPORT void updatestart_btn_clicked_cb(GtkWidget *w, gpointer data)
{
struct application *app = data;
GET_UI_ELEMENT(GtkButtonBox, update_dialog_btnBox);
app->updater_thread = g_thread_new("updater", updateThreadFunc, app);
gtk_widget_set_sensitive(GTK_WIDGET(update_dialog_btnBox), FALSE);
}
void deselect_adapter(struct application *app)
{
GET_UI_ELEMENT(GtkComboBox, cb_adapter_list);
GtkWidget *adapter_details = GTK_WIDGET( gtk_builder_get_object(app->builder, "adapterDetails") );
printf("deselect adapter\n");
if (app->current_adapter_handle) {
gcn64_closeDevice(app->current_adapter_handle);
app->current_adapter_handle = NULL;
gtk_widget_set_sensitive(adapter_details, FALSE);
}
gtk_combo_box_set_active_iter(cb_adapter_list, NULL);
}
void infoPopup(struct application *app, const char *message)
{
GtkWidget *dialog;
dialog = gtk_message_dialog_new(app->mainwindow,
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_INFO,
GTK_BUTTONS_CLOSE,
message);
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
}
void errorPopup(struct application *app, const char *message)
{
GtkWidget *dialog;
dialog = gtk_message_dialog_new(app->mainwindow,
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE,
message);
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
}
#define IHEX_MAX_FILE_SIZE 0x20000
char check_ihex_for_signature(const char *filename, const char *signature)
{
unsigned char *buf;
int max_address;
buf = malloc(IHEX_MAX_FILE_SIZE);
if (!buf) {
perror("malloc");
return 0;
}
max_address= load_ihex(filename, buf, IHEX_MAX_FILE_SIZE);
if (max_address > 0) {
if (!memmem(buf, max_address + 1, signature, strlen(signature))) {
return 0;
}
return 1;
}
return 0;
}
G_MODULE_EXPORT void update_usbadapter_firmware(GtkWidget *w, gpointer data)
{
struct application *app = data;
gint res;
GtkWidget *dialog = NULL;
GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN;
GET_UI_ELEMENT(GtkFileFilter, hexfilter);
GET_UI_ELEMENT(GtkDialog, firmware_update_dialog);
GET_UI_ELEMENT(GtkLabel, lbl_firmware_filename);
GET_UI_ELEMENT(GtkButtonBox, update_dialog_btnBox);
FILE *dfu_fp;
char *filename = NULL, *basename = NULL;
char adap_sig[64];
#ifndef WINDOWS
const char *notfound = "dfu-programmer not found. Cannot perform update.";
#else
const char *notfound = "dfu-programmer.exe not found. Cannot perform update.";
#endif
if (gcn64lib_getSignature(app->current_adapter_handle, adap_sig, sizeof(adap_sig))) {
errorPopup(app, "Could not read adapter signature - This file may not be meant for it (Bricking hazard!)");
goto done;
}
/* Test for dfu-programmer presence in path*/
dfu_fp = popen("dfu-programmer --version", "r");
//dfu_fp = popen("dfu2-programmer --version", "r");
if (!dfu_fp) {
perror("popen");
errorPopup(app, notfound);
return;
}
res = pclose(dfu_fp);
#ifdef WINDOWS
// Under Mingw, 0 is returned when dfu-programmmer was found
// and executed. Otherwise 1.
if (res != 0) {
#else
// Under Unix, the usual is available.
if (!WIFEXITED(res) || (WEXITSTATUS(res)!=1)) {
#endif
if (res) {
errorPopup(app, notfound);
return;
}
}
dialog = gtk_file_chooser_dialog_new("Open .hex file",
app->mainwindow,
action,
"_Cancel",
GTK_RESPONSE_CANCEL,
"_Open",
GTK_RESPONSE_ACCEPT,
NULL);
gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), hexfilter);
res = gtk_dialog_run (GTK_DIALOG(dialog));
if (res == GTK_RESPONSE_ACCEPT) {
GtkFileChooser *chooser = GTK_FILE_CHOOSER(dialog);
filename = gtk_file_chooser_get_filename(chooser);
basename = g_path_get_basename(filename);
gtk_widget_destroy(dialog);
dialog = NULL;
printf("File selected: %s\n", filename);
app->updateHexFile = filename;
if (!check_ihex_for_signature(filename, adap_sig)) {
errorPopup(app, "Signature not found - This file is invalid or not meant for this adapter");
goto done;
}
// For my development board based on at90usb1287. Need to pass the correct
// MCU argument to dfu-programmer
if (0 == strcmp("e106420a-7c54-11e5-ae9a-001bfca3c593", adap_sig)) {
app->at90usb1287 = 1;
} else {
app->at90usb1287 = 0;
}
/* Prepare the update dialog widgets... */
gtk_label_set_text(lbl_firmware_filename, basename);
gtk_widget_set_sensitive(GTK_WIDGET(update_dialog_btnBox), TRUE);
app->update_percent = 0;
app->update_status = "Ready";
updateProgress(data);
/* Run the dialog */
res = gtk_dialog_run(firmware_update_dialog);
gtk_widget_hide( GTK_WIDGET(firmware_update_dialog));
if (res == GTK_RESPONSE_OK) {
infoPopup(app, "Update succeeded.");
} else if (res == GTK_RESPONSE_REJECT) {
errorPopup(app, "Update failed. Suggestion: Do not disconnect the adapter and retry right away!");
}
printf("Update dialog done\n");
}
done:
if (filename)
g_free(filename);
if (basename)
g_free(basename);
if (dialog)
gtk_widget_destroy(dialog);
}
static gboolean periodic_updater(gpointer data)
{
struct application *app = data;
GET_UI_ELEMENT(GtkLabel, label_controller_type);
GET_UI_ELEMENT(GtkButton, btn_read_mempak);
GET_UI_ELEMENT(GtkButton, btn_write_mempak);
GET_UI_ELEMENT(GtkButton, btn_rumble_test);
if (app->current_adapter_handle && !app->inhibit_periodic_updates) {
app->controller_type = gcn64lib_getControllerType(app->current_adapter_handle, 0);
gtk_label_set_text(label_controller_type, gcn64lib_controllerName(app->controller_type));
switch (app->controller_type)
{
case CTL_TYPE_N64:
gtk_widget_set_sensitive(GTK_WIDGET(btn_read_mempak), TRUE);
gtk_widget_set_sensitive(GTK_WIDGET(btn_write_mempak), TRUE);
gtk_widget_set_sensitive(GTK_WIDGET(btn_rumble_test), TRUE);
break;
case CTL_TYPE_GC:
gtk_widget_set_sensitive(GTK_WIDGET(btn_rumble_test), TRUE);
gtk_widget_set_sensitive(GTK_WIDGET(btn_read_mempak), FALSE);
gtk_widget_set_sensitive(GTK_WIDGET(btn_write_mempak), FALSE);
break;
default:
case CTL_TYPE_NONE:
gtk_widget_set_sensitive(GTK_WIDGET(btn_read_mempak), FALSE);
gtk_widget_set_sensitive(GTK_WIDGET(btn_write_mempak), FALSE);
gtk_widget_set_sensitive(GTK_WIDGET(btn_rumble_test), FALSE);
break;
}
}
return TRUE;
}
static void updateGuiFromAdapter(struct application *app)
{
unsigned char buf[32];
int n;
struct {
unsigned char cfg_param;
GtkToggleButton *chkbtn;
} configurable_bits[] = {
// { CFG_PARAM_N64_SQUARE, GET_ELEMENT(GtkCheckButton, chkbtn_n64_square) },
// { CFG_PARAM_GC_MAIN_SQUARE, GET_ELEMENT(GtkCheckButton, chkbtn_gc_main_square) },
// { CFG_PARAM_GC_CSTICK_SQUARE, GET_ELEMENT(GtkCheckButton, chkbtn_gc_cstick_square) },
{ CFG_PARAM_FULL_SLIDERS, GET_ELEMENT(GtkToggleButton, chkbtn_gc_full_sliders) },
{ CFG_PARAM_INVERT_TRIG, GET_ELEMENT(GtkToggleButton, chkbtn_gc_invert_trig) },
{ },
};
GET_UI_ELEMENT(GtkLabel, label_product_name);
GET_UI_ELEMENT(GtkLabel, label_firmware_version);
GET_UI_ELEMENT(GtkLabel, label_usb_id);
GET_UI_ELEMENT(GtkLabel, label_device_path);
GET_UI_ELEMENT(GtkSpinButton, pollInterval0);
int i;
struct gcn64_info *info = &app->current_adapter_info;
char adap_sig[64];
if (!app->current_adapter_handle) {
deselect_adapter(app);
return;
}
if (gcn64lib_getSignature(app->current_adapter_handle, adap_sig, sizeof(adap_sig))) {
} else {
printf("Adapter signature: %s\n", adap_sig);
}
n = gcn64lib_getConfig(app->current_adapter_handle, CFG_PARAM_POLL_INTERVAL0, buf, sizeof(buf));
if (n == 1) {
printf("poll interval: %d\n", buf[0]);
gtk_spin_button_set_value(pollInterval0, (gdouble)buf[0]);
}
for (i=0; configurable_bits[i].chkbtn; i++) {
gcn64lib_getConfig(app->current_adapter_handle, configurable_bits[i].cfg_param, buf, sizeof(buf));
printf("config param %02x is %d\n", configurable_bits[i].cfg_param, buf[0]);
gtk_toggle_button_set_active(configurable_bits[i].chkbtn, buf[0]);
}
if (sizeof(wchar_t)==4) {
gtk_label_set_text(label_product_name, g_ucs4_to_utf8((void*)info->str_prodname, -1, NULL, NULL, NULL));
} else {
gtk_label_set_text(label_product_name, g_utf16_to_utf8((void*)info->str_prodname, -1, NULL, NULL, NULL));
}
if (0 == gcn64lib_getVersion(app->current_adapter_handle, (char*)buf, sizeof(buf))) {
sscanf((char*)buf, "%d.%d.%d", &app->firmware_maj, &app->firmware_min, &app->firmware_build);
gtk_label_set_text(label_firmware_version, (char*)buf);
}
snprintf((char*)buf, sizeof(buf), "%04x:%04x", info->usb_vid, info->usb_pid);
gtk_label_set_text(label_usb_id, (char*)buf);
gtk_label_set_text(label_device_path, info->str_path);
periodic_updater(app);
}
G_MODULE_EXPORT void pollIntervalChanged(GtkWidget *win, gpointer data)
{
struct application *app = data;
GET_UI_ELEMENT(GtkSpinButton, pollInterval0);
gdouble value;
int n;
unsigned char buf;
value = gtk_spin_button_get_value(pollInterval0);
printf("Value: %d\n", (int)value);
buf = (int)value;
n = gcn64lib_setConfig(app->current_adapter_handle, CFG_PARAM_POLL_INTERVAL0, &buf, 1);
if (n != 0) {
errorPopup(app, "Error setting configuration");
deselect_adapter(app);
rebuild_device_list_store(data);
}
}
G_MODULE_EXPORT void config_checkbox_changed(GtkWidget *win, gpointer data)
{
struct application *app = data;
struct {
unsigned char cfg_param;
GtkToggleButton *chkbtn;
} configurable_bits[] = {
// { CFG_PARAM_N64_SQUARE, GET_ELEMENT(GtkCheckButton, chkbtn_n64_square) },
// { CFG_PARAM_GC_MAIN_SQUARE, GET_ELEMENT(GtkCheckButton, chkbtn_gc_main_square) },
// { CFG_PARAM_GC_CSTICK_SQUARE, GET_ELEMENT(GtkCheckButton, chkbtn_gc_cstick_square) },
{ CFG_PARAM_FULL_SLIDERS, GET_ELEMENT(GtkToggleButton, chkbtn_gc_full_sliders) },
{ CFG_PARAM_INVERT_TRIG, GET_ELEMENT(GtkToggleButton, chkbtn_gc_invert_trig) },
{ },
};
int i, n;
unsigned char buf;
for (i=0; configurable_bits[i].chkbtn; i++) {
buf = gtk_toggle_button_get_active(configurable_bits[i].chkbtn);
n = gcn64lib_setConfig(app->current_adapter_handle, configurable_bits[i].cfg_param, &buf, 1);
if (n != 0) {
errorPopup(app, "Error setting configuration");
deselect_adapter(app);
rebuild_device_list_store(app);
break;
}
printf("cfg param %02x now set to %d\n", configurable_bits[i].cfg_param, buf);
}
}
gboolean rebuild_device_list_store(gpointer data)
{
struct application *app = data;
struct gcn64_list_ctx *listctx;
struct gcn64_info info;
GtkListStore *list_store;
GET_UI_ELEMENT(GtkComboBox, cb_adapter_list);
list_store = GTK_LIST_STORE( gtk_builder_get_object(app->builder, "adaptersList") );
gtk_list_store_clear(list_store);
printf("Listing device...\n");
listctx = gcn64_allocListCtx();
if (!listctx)
return FALSE;
while (gcn64_listDevices(&info, listctx)) {
GtkTreeIter iter;
printf("Device '%ls'\n", info.str_prodname);
gtk_list_store_append(list_store, &iter);
if (sizeof(wchar_t)==4) {
gtk_list_store_set(list_store, &iter,
0, g_ucs4_to_utf8((void*)info.str_serial, -1, NULL, NULL, NULL),
1, g_ucs4_to_utf8((void*)info.str_prodname, -1, NULL, NULL, NULL),
3, g_memdup(&info, sizeof(struct gcn64_info)),
-1);
} else {
gtk_list_store_set(list_store, &iter,
0, g_utf16_to_utf8((void*)info.str_serial, -1, NULL, NULL, NULL),
1, g_utf16_to_utf8((void*)info.str_prodname, -1, NULL, NULL, NULL),
3, g_memdup(&info, sizeof(struct gcn64_info)),
-1);
}
if (app->current_adapter_handle) {
if (!wcscmp(app->current_adapter_info.str_serial, info.str_serial)) {
gtk_combo_box_set_active_iter(cb_adapter_list, &iter);
}
}
}
gcn64_freeListCtx(listctx);
return FALSE;
}
G_MODULE_EXPORT void onMainWindowShow(GtkWidget *win, gpointer data)
{
int res;
struct application *app = data;
res = gcn64_init(1);
if (res) {
GtkWidget *d = GTK_WIDGET( gtk_builder_get_object(app->builder, "internalError") );
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(d), "gcn64_init failed (returned %d)", res);
gtk_widget_show(d);
return;
}
rebuild_device_list_store(data);
}
G_MODULE_EXPORT void adapterSelected(GtkComboBox *cb, gpointer data)
{
struct application *app = data;
GtkTreeIter iter;
GtkListStore *list_store = GTK_LIST_STORE( gtk_builder_get_object(app->builder, "adaptersList") );
GtkWidget *adapter_details = GTK_WIDGET( gtk_builder_get_object(app->builder, "adapterDetails") );
struct gcn64_info *info;
if (app->current_adapter_handle) {
gcn64_closeDevice(app->current_adapter_handle);
app->current_adapter_handle = NULL;
gtk_widget_set_sensitive(adapter_details, FALSE);
}
if (gtk_combo_box_get_active_iter(cb, &iter)) {
gtk_tree_model_get(GTK_TREE_MODEL(list_store), &iter, 3, &info, -1);
printf("%s\n", info->str_path);
app->current_adapter_handle = gcn64_openDevice(info);
if (!app->current_adapter_handle) {
errorPopup(app, "Failed to open adapter");
deselect_adapter(app);
return;
}
memcpy(&app->current_adapter_info, info, sizeof(struct gcn64_info));
updateGuiFromAdapter(app);
gtk_widget_set_sensitive(adapter_details, TRUE);
}
}
G_MODULE_EXPORT void onMainWindowHide(GtkWidget *win, gpointer data)
{
gcn64_shutdown();
}
G_MODULE_EXPORT void suspend_polling(GtkButton *button, gpointer data)
{
struct application *app = data;
gcn64lib_suspendPolling(app->current_adapter_handle, 1);
}
G_MODULE_EXPORT void resume_polling(GtkButton *button, gpointer data)
{
struct application *app = data;
gcn64lib_suspendPolling(app->current_adapter_handle, 0);
}
G_MODULE_EXPORT void onFileRescan(GtkWidget *wid, gpointer data)
{
rebuild_device_list_store(data);
}
static int mempak_io_progress_cb(int progress, void *ctx)
{
struct application *app = ctx;
GET_UI_ELEMENT(GtkProgressBar, mempak_io_progress);
gtk_progress_bar_set_fraction(mempak_io_progress, progress/((gdouble)MEMPAK_MEM_SIZE));
while (gtk_events_pending()) {
gtk_main_iteration_do(FALSE);
}
return app->stop_mempak_io;
}
G_MODULE_EXPORT void mempak_io_stop(GtkWidget *wid, gpointer data)
{
struct application *app = data;
app->stop_mempak_io = 1;
}
G_MODULE_EXPORT void write_n64_pak(GtkWidget *wid, gpointer data)
{
struct application *app = data;
GET_UI_ELEMENT(GtkWindow, win_mempak_edit);
GET_UI_ELEMENT(GtkDialog, mempak_io_dialog);
GET_UI_ELEMENT(GtkLabel, mempak_op_label);
GtkWidget *confirm_dialog;
gint res;
gtk_widget_show(GTK_WIDGET(win_mempak_edit));
confirm_dialog = gtk_message_dialog_new(GTK_WINDOW(win_mempak_edit), GTK_DIALOG_MODAL,
GTK_MESSAGE_QUESTION, 0, "Your memory card will be completely overwritten by the content of the memory pack editor.\n\nAre you sure?");
gtk_dialog_add_buttons(GTK_DIALOG(confirm_dialog), "Cancel", 1, "Continue", 2, NULL);
res = gtk_dialog_run(GTK_DIALOG(confirm_dialog));
gtk_widget_destroy(confirm_dialog);
switch(res)
{
case 2:
printf("Confirmed write N64 mempak.\n");
app->stop_mempak_io = 0;
gtk_label_set_text(mempak_op_label, "Writing memory pack...");
gtk_widget_show(GTK_WIDGET(mempak_io_dialog));
res = gcn64lib_mempak_upload(app->current_adapter_handle, 0, mpke_getCurrentMempak(app), mempak_io_progress_cb, app);
gtk_widget_hide(GTK_WIDGET(mempak_io_dialog));
if (res != 0) {
switch(res)
{
case -1: errorPopup(app, "No mempak detected"); break;
case -2: errorPopup(app, "I/O error writing to mempak"); break;
case -4: errorPopup(app, "Write aborted"); break;
default:
errorPopup(app, "Error writing to mempak"); break;
}
}
break;
case 1:
default:
printf("Write N64 mempak cancelled\n");
}
}
G_MODULE_EXPORT void read_n64_pak(GtkWidget *wid, gpointer data)
{
struct application *app = data;
GET_UI_ELEMENT(GtkWindow, win_mempak_edit);
GET_UI_ELEMENT(GtkDialog, mempak_io_dialog);
GET_UI_ELEMENT(GtkLabel, mempak_op_label);
mempak_structure_t *mpk;
int res;
printf("N64 read mempak\n");
if (!app->current_adapter_handle)
return;
gtk_widget_show(GTK_WIDGET(mempak_io_dialog));
gtk_label_set_text(mempak_op_label, "Reading memory pack...");
app->stop_mempak_io = 0;
res = gcn64lib_mempak_download(app->current_adapter_handle, 0, &mpk, mempak_io_progress_cb, app);
gtk_widget_hide(GTK_WIDGET(mempak_io_dialog));
if (res != 0) {
switch(res)
{
case -1: errorPopup(app, "No mempak detected"); break;
case -2: errorPopup(app, "I/O error reading mempak"); break;
case -4: errorPopup(app, "Read aborted"); break;
default:
case -3: errorPopup(app, "Error reading mempak"); break;
}
}
else {
mpke_replaceMpk(app, mpk, NULL);
gtk_widget_show(GTK_WIDGET(win_mempak_edit));
}
}
G_MODULE_EXPORT void testVibration(GtkWidget *wid, gpointer data)
{
struct application *app = data;
GtkWidget *dialog;
if ((app->firmware_maj < 3) || (app->firmware_min < 1)) {
errorPopup(app, "Firmware 3.1 or later required");
} else {
if (0 > gcn64lib_forceVibration(app->current_adapter_handle, 0, 1)) {
errorPopup(app, "Error setting vibration");
} else {
dialog = gtk_dialog_new_with_buttons("Vibration test", app->mainwindow, GTK_DIALOG_MODAL, "Stop vibration", GTK_RESPONSE_ACCEPT, NULL);
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
gcn64lib_forceVibration(app->current_adapter_handle, 0, 0);
}
}
}
int
main( int argc,
char **argv )
{
GtkWindow *window;
GError *error = NULL;
struct application app = { };
/* Init GTK+ */
gtk_init( &argc, &argv );
/* Create new GtkBuilder object */
app.builder = gtk_builder_new();
/* Load UI from file. If error occurs, report it and quit application.
* Replace "tut.glade" with your saved project. */
if( ! gtk_builder_add_from_file( app.builder, "gui.xml", &error ) )
{
g_warning( "%s", error->message );
g_free( error );
return( 1 );
}
app.mpke = mpkedit_new(&app);
/* Get main window pointer from UI */
window = GTK_WINDOW( gtk_builder_get_object( app.builder, "mainWindow" ) );
app.mainwindow = window;
/* Connect signals */
gtk_builder_connect_signals( app.builder, &app );
g_timeout_add_seconds(1, periodic_updater, &app);
/* Show window. All other widgets are automatically shown by GtkBuilder */
gtk_widget_show( GTK_WIDGET(window) );
/* Start main loop */
gtk_main();
mpkedit_free(app.mpke);
return( 0 );
}

View File

@ -1,34 +0,0 @@
#include <glib.h>
#include <gtk/gtk.h>
#include "gcn64.h"
#include "gcn64lib.h"
#include "gcn64ctl_gui_mpkedit.h"
#define GET_ELEMENT(TYPE, ELEMENT) (TYPE *)gtk_builder_get_object(app->builder, #ELEMENT)
#define GET_UI_ELEMENT(TYPE, ELEMENT) TYPE *ELEMENT = GET_ELEMENT(TYPE, ELEMENT)
void errorPopup(struct application *app, const char *message);
struct application {
GtkBuilder *builder;
GtkWindow *mainwindow;
gcn64_hdl_t current_adapter_handle;
struct gcn64_info current_adapter_info;
GThread *updater_thread;
const char *update_status;
const char *updateHexFile;
int update_percent;
int update_dialog_response;
struct mpkedit_data *mpke;
int stop_mempak_io;
int inhibit_periodic_updates;
int controller_type;
int firmware_maj, firmware_min, firmware_build;
int at90usb1287;
};

View File

@ -1,443 +0,0 @@
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <libgen.h>
#include "gcn64ctl_gui.h"
#include "mempak.h"
void mpke_syncModel(struct application *app);
struct mpkedit_data {
struct mempak_structure *mpk;
char *filename;
int modified;
};
struct mpkedit_data *mpkedit_new(struct application *app)
{
struct mpkedit_data *mpke;
mpke = calloc(sizeof(struct mpkedit_data), 1);
if (!mpke) {
perror("calloc");
return NULL;
}
mpke->mpk = mempak_new();
if (!mpke->mpk) {
free(mpke);
return NULL;
}
return mpke;
}
void mpkedit_free(struct mpkedit_data *mpke)
{
if (mpke) {
free(mpke);
}
}
int mpke_getSelection(struct application *app)
{
GET_UI_ELEMENT(GtkTreeView, n64_notes_treeview);
GtkTreeSelection *sel = gtk_tree_view_get_selection(n64_notes_treeview);
GList *selected;
int sel_id = -1;
if (!sel) {
return -1;
}
selected = gtk_tree_selection_get_selected_rows(sel, NULL);
if (selected && selected->data) {
GtkTreePath *path;
gint *indices;
path = (GtkTreePath*)selected->data;
indices = gtk_tree_path_get_indices(path);
sel_id = indices[0];
} else {
return -1;
}
if (selected)
g_list_free_full(selected, (GDestroyNotify)gtk_tree_path_free);
printf("Current selection: %d\n", sel_id);
return sel_id;
}
void mpke_syncTitle(struct application *app)
{
GET_UI_ELEMENT(GtkWindow, win_mempak_edit);
char titlebuf[64];
if (app->mpke->filename) {
char *bn = g_path_get_basename(app->mpke->filename);
snprintf(titlebuf, sizeof(titlebuf), "N64 Mempak editor - %s%s",
bn,
app->mpke->modified ? " [MODIFIED]":""
);
g_free(bn);
printf("New title: %s\n", titlebuf);
gtk_window_set_title(win_mempak_edit, titlebuf);
} else {
snprintf(titlebuf, sizeof(titlebuf), "N64 Mempak editor%s",
app->mpke->modified ? " [NOT SAVED]" : "");
}
}
void mpke_updateFilename(struct application *app, char *filename)
{
if (app->mpke->filename) {
// The filename always comes from gtk_file_chooser_get_filename
g_free(app->mpke->filename);
}
app->mpke->filename = filename;
mpke_syncTitle(app);
}
void mpke_replaceMpk(struct application *app, mempak_structure_t *mpk, char *filename)
{
if (app->mpke->mpk) {
mempak_free(app->mpke->mpk);
}
app->mpke->mpk = mpk;
mpke_syncModel(app);
mpke_updateFilename(app, filename);
}
void mpke_syncModel(struct application *app)
{
GET_UI_ELEMENT(GtkListStore, n64_notes);
GET_UI_ELEMENT(GtkTreeView, n64_notes_treeview);
GET_UI_ELEMENT(GtkStatusbar, mempak_status_bar);
int i, res;
char statusbuf[64];
gtk_list_store_clear(n64_notes);
if (!app->mpke->mpk) {
return;
}
for (i=0; i<16; i++) {
GtkTreeIter iter;
entry_structure_t note_data;
gtk_list_store_append(n64_notes, &iter);
res = get_mempak_entry(app->mpke->mpk, i, &note_data);
if (res) {
gtk_list_store_set(n64_notes, &iter, 0, i, 1, "!!ERROR!!", 2, 0, -1);
} else {
if (note_data.valid) {
gtk_list_store_set(n64_notes, &iter, 0, i, 1, note_data.name, 2, note_data.blocks, 3, app->mpke->mpk->note_comments[i], -1);
} else {
gtk_list_store_set(n64_notes, &iter, 0, i, -1);
}
}
}
gtk_tree_view_set_model(n64_notes_treeview, GTK_TREE_MODEL(n64_notes));
snprintf(statusbuf, sizeof(statusbuf), "Blocks used: %d / %d", 123-get_mempak_free_space(app->mpke->mpk), 123);
gtk_statusbar_push(mempak_status_bar, gtk_statusbar_get_context_id(mempak_status_bar, "free blocks"), statusbuf);
}
G_MODULE_EXPORT void mpke_export_note(GtkWidget *win, gpointer data)
{
struct application *app = data;
int selection;
GtkWidget *dialog;
GtkFileChooser *chooser;
GET_UI_ELEMENT(GtkWindow, win_mempak_edit);
GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SAVE;
GET_UI_ELEMENT(GtkFileFilter, n64_note_filter);
int res;
selection = mpke_getSelection(app);
if (selection <0) {
printf("No selection");
return;
}
if (app->mpke->mpk) {
entry_structure_t entry;
if (0==get_mempak_entry(app->mpke->mpk, selection, &entry)) {
char namebuf[64];
if (!entry.valid) {
errorPopup(app, "Please select a non-empty note");
return;
}
dialog = gtk_file_chooser_dialog_new("Save File",
win_mempak_edit,
action,
"_Cancel",
GTK_RESPONSE_CANCEL,
"_Save",
GTK_RESPONSE_ACCEPT,
NULL);
chooser = GTK_FILE_CHOOSER(dialog);
snprintf(namebuf, sizeof(namebuf), "%s.note", entry.name);
gtk_file_chooser_set_current_name(chooser, namebuf);
gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), n64_note_filter);
res = gtk_dialog_run (GTK_DIALOG(dialog));
if (res == GTK_RESPONSE_ACCEPT) {
char *filename;
filename = gtk_file_chooser_get_filename(chooser);
if (mempak_exportNote(app->mpke->mpk, selection, filename)) {
errorPopup(app, "Could not export note");
} else {
printf("Note saved to %s\n", filename);
}
}
gtk_widget_destroy(dialog);
}
}
}
G_MODULE_EXPORT void mpke_insert_note(GtkWidget *win, gpointer data)
{
struct application *app = data;
GtkWidget *dialog;
GET_UI_ELEMENT(GtkWindow, win_mempak_edit);
GET_UI_ELEMENT(GtkFileFilter, n64_note_filter);
GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN;
int res;
dialog = gtk_file_chooser_dialog_new("Load N64 mempak image",
win_mempak_edit,
action,
"_Cancel",
GTK_RESPONSE_CANCEL,
"_Open",
GTK_RESPONSE_ACCEPT,
NULL);
gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), n64_note_filter);
res = gtk_dialog_run (GTK_DIALOG(dialog));
if (res == GTK_RESPONSE_ACCEPT) {
char *filename;
GtkFileChooser *chooser = GTK_FILE_CHOOSER(dialog);
entry_structure_t entry;
int used_note_id;
int dst_id;
filename = gtk_file_chooser_get_filename(chooser);
dst_id = mpke_getSelection(app);
if (0 == get_mempak_entry(app->mpke->mpk, dst_id, &entry)) {
if (entry.valid) {
// Ask confirmation
printf("Ask confirmation\n");
}
}
res = mempak_importNote(app->mpke->mpk, filename, dst_id, &used_note_id);
if (res) {
switch(res)
{
default:
case -1: errorPopup(app, "Error loading file or inserting note\n"); break;
case -2: errorPopup(app, "Not enough free blocks to insert note\n"); break;
}
} else {
// Success
app->mpke->modified =1;
mpke_syncModel(app);
mpke_syncTitle(app);
}
}
gtk_widget_destroy(dialog);
}
G_MODULE_EXPORT void mpke_new(GtkWidget *win, gpointer data)
{
struct application *app = data;
app->mpke->modified = 0;
mpke_replaceMpk(app, mempak_new(), NULL);
}
G_MODULE_EXPORT void mpke_open(GtkWidget *win, gpointer data)
{
struct application *app = data;
GtkWidget *dialog;
GET_UI_ELEMENT(GtkWindow, win_mempak_edit);
GET_UI_ELEMENT(GtkFileFilter, n64_mempak_filter);
GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN;
int res;
dialog = gtk_file_chooser_dialog_new("Load N64 mempak image",
win_mempak_edit,
action,
"_Cancel",
GTK_RESPONSE_CANCEL,
"_Open",
GTK_RESPONSE_ACCEPT,
NULL);
gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), n64_mempak_filter);
res = gtk_dialog_run (GTK_DIALOG(dialog));
if (res == GTK_RESPONSE_ACCEPT) {
char *filename;
GtkFileChooser *chooser = GTK_FILE_CHOOSER(dialog);
mempak_structure_t *mpk;
filename = gtk_file_chooser_get_filename(chooser);
mpk = mempak_loadFromFile(filename);
if (mpk) {
app->mpke->modified = 0;
mpke_replaceMpk(app, mpk, filename);
} else {
errorPopup(app, "Failed to load mempak");
}
}
gtk_widget_destroy(dialog);
}
G_MODULE_EXPORT void mpke_saveas(GtkWidget *win, gpointer data)
{
struct application *app = data;
GtkWidget *dialog;
GtkFileChooser *chooser;
GET_UI_ELEMENT(GtkWindow, win_mempak_edit);
GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SAVE;
GET_UI_ELEMENT(GtkFileFilter, n64_mempak_filter);
int res;
dialog = gtk_file_chooser_dialog_new("Save File",
win_mempak_edit,
action,
"_Cancel",
GTK_RESPONSE_CANCEL,
"_Save",
GTK_RESPONSE_ACCEPT,
NULL);
chooser = GTK_FILE_CHOOSER(dialog);
gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), n64_mempak_filter);
if (app->mpke->filename) {
gchar *bs = g_path_get_basename(app->mpke->filename);
gchar *dn = g_path_get_dirname(app->mpke->filename);
gtk_file_chooser_set_current_name(chooser, bs);
gtk_file_chooser_set_current_folder(chooser, dn);
g_free(bs);
g_free(dn);
} else {
gtk_file_chooser_set_current_name(chooser, "mempak.n64");
}
res = gtk_dialog_run (GTK_DIALOG(dialog));
if (res == GTK_RESPONSE_ACCEPT) {
char *filename;
int fmt;
filename = gtk_file_chooser_get_filename(chooser);
fmt = mempak_getFilenameFormat(filename);
if (fmt!= MPK_FORMAT_INVALID) {
mempak_saveToFile(app->mpke->mpk, filename, fmt);
printf("Saved to %s\n", filename);
app->mpke->modified = 0;
mpke_updateFilename(app,filename);
} else {
errorPopup(app, "Unknown file format specified");
}
}
gtk_widget_destroy(dialog);
}
G_MODULE_EXPORT void mpke_save(GtkWidget *win, gpointer data)
{
struct application *app = data;
if (!app->mpke->mpk)
return;
if (!app->mpke->filename) {
mpke_saveas(win, data);
} else {
mempak_saveToFile(app->mpke->mpk, app->mpke->filename, app->mpke->mpk->file_format);
app->mpke->modified = 0;
mpke_syncTitle(app);
}
}
G_MODULE_EXPORT void mpke_delete(GtkWidget *win, gpointer data)
{
struct application *app = data;
int selection;
selection = mpke_getSelection(app);
if (selection <0) {
printf("No selection");
return;
}
if (app->mpke->mpk) {
entry_structure_t entry;
if (0==get_mempak_entry(app->mpke->mpk, selection, &entry)) {
delete_mempak_entry(app->mpke->mpk, &entry);
mpke_syncModel(app);
app->mpke->modified = 1;
mpke_syncTitle(app);
}
}
}
G_MODULE_EXPORT void n64_note_activated(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer data)
{
struct application *app = data;
printf("Yo!\n");
}
G_MODULE_EXPORT void onMempakWindowShow(GtkWidget *win, gpointer data)
{
struct application *app = data;
mpke_syncModel(app);
}
mempak_structure_t *mpke_getCurrentMempak(struct application *app)
{
return app->mpke->mpk;
}

View File

@ -1,14 +0,0 @@
#ifndef gcn64ctl_gui_mpkedit_h__
#define gcn64ctl_gui_mpkedit_h__
#include "mempak.h"
struct mpkedit_data;
struct application;
struct mpkedit_data *mpkedit_new(struct application *app);
void mpkedit_free(struct mpkedit_data *mpke);
void mpke_replaceMpk(struct application *app, mempak_structure_t *mpk, char *filename);
mempak_structure_t *mpke_getCurrentMempak(struct application *app);
#endif

View File

@ -1,330 +0,0 @@
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <stdio.h>
#include "gcn64lib.h"
#include "../requests.h"
#include "../gcn64_protocol.h"
#include "hexdump.h"
int gcn64lib_getConfig(gcn64_hdl_t hdl, unsigned char param, unsigned char *rx, unsigned char rx_max)
{
unsigned char cmd[2];
int n;
if (!hdl) {
return -1;
}
cmd[0] = RQ_GCN64_GET_CONFIG_PARAM;
cmd[1] = param;
n = gcn64_exchange(hdl, cmd, 2, rx, rx_max);
if (n<2)
return n;
n -= 2;
// Drop the leading CMD and PARAM
if (n) {
memmove(rx, rx+2, n);
}
return n;
}
int gcn64lib_setConfig(gcn64_hdl_t hdl, unsigned char param, unsigned char *data, unsigned char len)
{
unsigned char cmd[2 + len];
int n;
if (!hdl) {
return -1;
}
cmd[0] = RQ_GCN64_SET_CONFIG_PARAM;
cmd[1] = param;
memcpy(cmd + 2, data, len);
n = gcn64_exchange(hdl, cmd, 2 + len, cmd, sizeof(cmd));
if (n<0)
return n;
return 0;
}
int gcn64lib_suspendPolling(gcn64_hdl_t hdl, unsigned char suspend)
{
unsigned char cmd[2];
int n;
if (!hdl) {
return -1;
}
cmd[0] = RQ_GCN64_SUSPEND_POLLING;
cmd[1] = suspend;
n = gcn64_exchange(hdl, cmd, 2, cmd, sizeof(cmd));
if (n<0)
return n;
return 0;
}
int gcn64lib_getVersion(gcn64_hdl_t hdl, char *dst, int dstmax)
{
unsigned char cmd[32];
int n;
if (!hdl) {
return -1;
}
if (dstmax <= 0)
return -1;
cmd[0] = RQ_GCN64_GET_VERSION;
n = gcn64_exchange(hdl, cmd, 1, cmd, sizeof(cmd));
if (n<0)
return n;
dst[0] = 0;
if (n > 1) {
strncpy(dst, (char*)cmd+1, n);
}
dst[dstmax-1] = 0;
return 0;
}
int gcn64lib_getControllerType(gcn64_hdl_t hdl, int chn)
{
unsigned char cmd[32];
int n;
if (!hdl) {
return -1;
}
cmd[0] = RQ_GCN64_GET_CONTROLLER_TYPE;
cmd[1] = chn;
n = gcn64_exchange(hdl, cmd, 2, cmd, sizeof(cmd));
if (n<0)
return n;
if (n<3)
return -1;
return cmd[2];
}
const char *gcn64lib_controllerName(int type)
{
switch(type) {
case CTL_TYPE_NONE: return "No controller";
case CTL_TYPE_N64: return "N64 Controller";
case CTL_TYPE_GC: return "GC Controller";
case CTL_TYPE_GCKB: return "GC Keyboard";
default:
return "Unknown";
}
}
int gcn64lib_getSignature(gcn64_hdl_t hdl, char *dst, int dstmax)
{
unsigned char cmd[40];
int n;
if (!hdl) {
return -1;
}
if (dstmax <= 0)
return -1;
cmd[0] = RQ_GCN64_GET_SIGNATURE;
n = gcn64_exchange(hdl, cmd, 1, cmd, sizeof(cmd));
if (n<0)
return n;
dst[0] = 0;
if (n > 1) {
strncpy(dst, (char*)cmd+1, n);
}
dst[dstmax-1] = 0;
return 0;
}
int gcn64lib_forceVibration(gcn64_hdl_t hdl, unsigned char channel, unsigned char vibrate)
{
unsigned char cmd[3];
int n;
if (!hdl) {
return -1;
}
cmd[0] = RQ_GCN64_SET_VIBRATION;
cmd[1] = channel;
cmd[2] = vibrate;
n = gcn64_exchange(hdl, cmd, 3, cmd, sizeof(cmd));
if (n<0)
return n;
return 0;
}
int gcn64lib_rawSiCommand(gcn64_hdl_t hdl, unsigned char channel, unsigned char *tx, unsigned char tx_len, unsigned char *rx, unsigned char max_rx)
{
unsigned char cmd[3 + tx_len];
unsigned char rep[3 + 64];
int cmdlen, rx_len, n;
if (!hdl) {
return -1;
}
if (!tx) {
return -1;
}
cmd[0] = RQ_GCN64_RAW_SI_COMMAND;
cmd[1] = channel;
cmd[2] = tx_len;
memcpy(cmd+3, tx, tx_len);
cmdlen = 3 + tx_len;
n = gcn64_exchange(hdl, cmd, cmdlen, rep, sizeof(rep));
if (n<0)
return n;
rx_len = rep[2];
if (rx) {
memcpy(rx, rep + 3, rx_len);
}
return rx_len;
}
int gcn64lib_16bit_scan(gcn64_hdl_t hdl, unsigned short min, unsigned short max)
{
int id, n;
unsigned char buf[64];
if (!hdl) {
return -1;
}
for (id = min; id<=max; id++) {
buf[0] = id >> 8;
buf[1] = id & 0xff;
n = gcn64lib_rawSiCommand(hdl, 0, buf, 2, buf, sizeof(buf));
if (n > 0) {
printf("CMD 0x%04x answer: ", id);
printHexBuf(buf, n);
}
}
return 0;
}
int gcn64lib_8bit_scan(gcn64_hdl_t hdl, unsigned char min, unsigned char max)
{
int id, n;
unsigned char buf[64];
if (!hdl) {
return -1;
}
for (id = min; id<=max; id++) {
buf[0] = id;
n = gcn64lib_rawSiCommand(hdl, 0, buf, 1, buf, sizeof(buf));
if (n > 0) {
printf("CMD 0x%02x answer: ", id);
printHexBuf(buf, n);
}
}
return 0;
}
int gcn64lib_bootloader(gcn64_hdl_t hdl)
{
unsigned char cmd[4];
int cmdlen;
if (!hdl) {
return -1;
}
cmd[0] = RQ_GCN64_JUMP_TO_BOOTLOADER;
cmdlen = 1;
gcn64_exchange(hdl, cmd, cmdlen, cmd, sizeof(cmd));
return 0;
}
int gcn64lib_n64_expansionWrite(gcn64_hdl_t hdl, unsigned short addr, unsigned char *data, int len)
{
unsigned char cmd[3 + len];
int cmdlen;
int n;
if (!hdl) {
return -1;
}
cmd[0] = N64_EXPANSION_WRITE;
cmd[1] = addr>>8; // Address high byte
cmd[2] = addr&0xff; // Address low byte
memcpy(cmd + 3, data, len);
cmdlen = 3 + len;
n = gcn64lib_rawSiCommand(hdl, 0, cmd, cmdlen, cmd, sizeof(cmd));
if (n != 1) {
printf("expansion write returned != 1 (%d)\n", n);
return -1;
}
return cmd[0];
}
int gcn64lib_n64_expansionRead(gcn64_hdl_t hdl, unsigned short addr, unsigned char *dst, int max_len)
{
unsigned char cmd[3];
int n;
if (!hdl) {
return -1;
}
cmd[0] = N64_EXPANSION_READ;
cmd[1] = addr>>8; // Address high byte
cmd[2] = addr&0xff; // Address low byte
n = gcn64lib_rawSiCommand(hdl, 0, cmd, 3, dst, max_len);
if (n < 0)
return n;
return n;
}

View File

@ -1,28 +0,0 @@
#ifndef _gcn64_lib_h__
#define _gcn64_lib_h__
#include "gcn64.h"
#define CTL_TYPE_NONE 0
#define CTL_TYPE_N64 1
#define CTL_TYPE_GC 2
#define CTL_TYPE_GCKB 3
int gcn64lib_suspendPolling(gcn64_hdl_t hdl, unsigned char suspend);
int gcn64lib_setConfig(gcn64_hdl_t hdl, unsigned char param, unsigned char *data, unsigned char len);
int gcn64lib_getConfig(gcn64_hdl_t hdl, unsigned char param, unsigned char *rx, unsigned char rx_max);
int gcn64lib_rawSiCommand(gcn64_hdl_t hdl, unsigned char channel, unsigned char *tx, unsigned char tx_len, unsigned char *rx, unsigned char max_rx);
int gcn64lib_getVersion(gcn64_hdl_t hdl, char *dst, int dstmax);
int gcn64lib_getSignature(gcn64_hdl_t hdl, char *dst, int dstmax);
int gcn64lib_forceVibration(gcn64_hdl_t hdl, unsigned char channel, unsigned char vibrate);
int gcn64lib_getControllerType(gcn64_hdl_t hdl, int chn);
const char *gcn64lib_controllerName(int type);
int gcn64lib_bootloader(gcn64_hdl_t hdl);
int gcn64lib_n64_expansionWrite(gcn64_hdl_t hdl, unsigned short addr, unsigned char *data, int len);
int gcn64lib_n64_expansionRead(gcn64_hdl_t hdl, unsigned short addr, unsigned char *dst, int max_len);
int gcn64lib_8bit_scan(gcn64_hdl_t hdl, unsigned char min, unsigned char max);
int gcn64lib_16bit_scan(gcn64_hdl_t hdl, unsigned short min, unsigned short max);
#endif // _gcn64_lib_h__

View File

@ -1,5 +0,0 @@
#ifndef _gcn64_utils_h__
#define _gcn64_utils_h__
#endif // _gcn64_utils_h__

View File

@ -1,29 +0,0 @@
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
void printHexBuf(unsigned char *buf, int n)
{
int i;
for (i=0; i<n; i++) {
printf("%02x ", buf[i]);
}
printf("\n");
}

View File

@ -1,3 +0,0 @@
void printHexBuf(unsigned char *buf, int n);

View File

@ -1,939 +0,0 @@
/*******************************************************
HIDAPI - Multi-Platform library for
communication with HID devices.
Alan Ott
Signal 11 Software
8/22/2009
Copyright 2009, All Rights Reserved.
At the discretion of the user of this library,
this software may be licensed under the terms of the
GNU General Public License v3, a BSD-Style license, or the
original HIDAPI license as outlined in the LICENSE.txt,
LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
files located at the root of the source distribution.
These files may also be found in the public source
code repository located at:
http://github.com/signal11/hidapi .
********************************************************/
#include <windows.h>
#ifndef _NTDEF_
typedef LONG NTSTATUS;
#endif
#ifdef __MINGW32__
#include <ntdef.h>
#include <winbase.h>
#endif
#ifdef __CYGWIN__
#include <ntdef.h>
#define _wcsdup wcsdup
#endif
/*#define HIDAPI_USE_DDK*/
#ifdef __cplusplus
extern "C" {
#endif
#include <setupapi.h>
#include <winioctl.h>
#ifdef HIDAPI_USE_DDK
#include <hidsdi.h>
#endif
/* Copied from inc/ddk/hidclass.h, part of the Windows DDK. */
#define HID_OUT_CTL_CODE(id) \
CTL_CODE(FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
#define IOCTL_HID_GET_FEATURE HID_OUT_CTL_CODE(100)
#ifdef __cplusplus
} /* extern "C" */
#endif
#include <stdio.h>
#include <stdlib.h>
#include "hidapi.h"
#ifdef _MSC_VER
/* Thanks Microsoft, but I know how to use strncpy(). */
#pragma warning(disable:4996)
#endif
#ifdef __cplusplus
extern "C" {
#endif
#ifndef HIDAPI_USE_DDK
/* Since we're not building with the DDK, and the HID header
files aren't part of the SDK, we have to define all this
stuff here. In lookup_functions(), the function pointers
defined below are set. */
typedef struct _HIDD_ATTRIBUTES{
ULONG Size;
USHORT VendorID;
USHORT ProductID;
USHORT VersionNumber;
} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES;
typedef USHORT USAGE;
typedef struct _HIDP_CAPS {
USAGE Usage;
USAGE UsagePage;
USHORT InputReportByteLength;
USHORT OutputReportByteLength;
USHORT FeatureReportByteLength;
USHORT Reserved[17];
USHORT fields_not_used_by_hidapi[10];
} HIDP_CAPS, *PHIDP_CAPS;
typedef void* PHIDP_PREPARSED_DATA;
#define HIDP_STATUS_SUCCESS 0x110000
typedef BOOLEAN (__stdcall *HidD_GetAttributes_)(HANDLE device, PHIDD_ATTRIBUTES attrib);
typedef BOOLEAN (__stdcall *HidD_GetSerialNumberString_)(HANDLE device, PVOID buffer, ULONG buffer_len);
typedef BOOLEAN (__stdcall *HidD_GetManufacturerString_)(HANDLE handle, PVOID buffer, ULONG buffer_len);
typedef BOOLEAN (__stdcall *HidD_GetProductString_)(HANDLE handle, PVOID buffer, ULONG buffer_len);
typedef BOOLEAN (__stdcall *HidD_SetFeature_)(HANDLE handle, PVOID data, ULONG length);
typedef BOOLEAN (__stdcall *HidD_GetFeature_)(HANDLE handle, PVOID data, ULONG length);
typedef BOOLEAN (__stdcall *HidD_GetIndexedString_)(HANDLE handle, ULONG string_index, PVOID buffer, ULONG buffer_len);
typedef BOOLEAN (__stdcall *HidD_GetPreparsedData_)(HANDLE handle, PHIDP_PREPARSED_DATA *preparsed_data);
typedef BOOLEAN (__stdcall *HidD_FreePreparsedData_)(PHIDP_PREPARSED_DATA preparsed_data);
typedef NTSTATUS (__stdcall *HidP_GetCaps_)(PHIDP_PREPARSED_DATA preparsed_data, HIDP_CAPS *caps);
typedef BOOLEAN (__stdcall *HidD_SetNumInputBuffers_)(HANDLE handle, ULONG number_buffers);
static HidD_GetAttributes_ HidD_GetAttributes;
static HidD_GetSerialNumberString_ HidD_GetSerialNumberString;
static HidD_GetManufacturerString_ HidD_GetManufacturerString;
static HidD_GetProductString_ HidD_GetProductString;
static HidD_SetFeature_ HidD_SetFeature;
static HidD_GetFeature_ HidD_GetFeature;
static HidD_GetIndexedString_ HidD_GetIndexedString;
static HidD_GetPreparsedData_ HidD_GetPreparsedData;
static HidD_FreePreparsedData_ HidD_FreePreparsedData;
static HidP_GetCaps_ HidP_GetCaps;
static HidD_SetNumInputBuffers_ HidD_SetNumInputBuffers;
static HMODULE lib_handle = NULL;
static BOOLEAN initialized = FALSE;
#endif /* HIDAPI_USE_DDK */
struct hid_device_ {
HANDLE device_handle;
BOOL blocking;
USHORT output_report_length;
size_t input_report_length;
void *last_error_str;
DWORD last_error_num;
BOOL read_pending;
char *read_buf;
OVERLAPPED ol;
};
static hid_device *new_hid_device()
{
hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device));
dev->device_handle = INVALID_HANDLE_VALUE;
dev->blocking = TRUE;
dev->output_report_length = 0;
dev->input_report_length = 0;
dev->last_error_str = NULL;
dev->last_error_num = 0;
dev->read_pending = FALSE;
dev->read_buf = NULL;
memset(&dev->ol, 0, sizeof(dev->ol));
dev->ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*inital state f=nonsignaled*/, NULL);
return dev;
}
static void free_hid_device(hid_device *dev)
{
CloseHandle(dev->ol.hEvent);
CloseHandle(dev->device_handle);
LocalFree(dev->last_error_str);
free(dev->read_buf);
free(dev);
}
static void register_error(hid_device *device, const char *op)
{
WCHAR *ptr, *msg;
FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPVOID)&msg, 0/*sz*/,
NULL);
/* Get rid of the CR and LF that FormatMessage() sticks at the
end of the message. Thanks Microsoft! */
ptr = msg;
while (*ptr) {
if (*ptr == '\r') {
*ptr = 0x0000;
break;
}
ptr++;
}
/* Store the message off in the Device entry so that
the hid_error() function can pick it up. */
LocalFree(device->last_error_str);
device->last_error_str = msg;
}
#ifndef HIDAPI_USE_DDK
static int lookup_functions()
{
lib_handle = LoadLibraryA("hid.dll");
if (lib_handle) {
#define RESOLVE(x) x = (x##_)GetProcAddress(lib_handle, #x); if (!x) return -1;
RESOLVE(HidD_GetAttributes);
RESOLVE(HidD_GetSerialNumberString);
RESOLVE(HidD_GetManufacturerString);
RESOLVE(HidD_GetProductString);
RESOLVE(HidD_SetFeature);
RESOLVE(HidD_GetFeature);
RESOLVE(HidD_GetIndexedString);
RESOLVE(HidD_GetPreparsedData);
RESOLVE(HidD_FreePreparsedData);
RESOLVE(HidP_GetCaps);
RESOLVE(HidD_SetNumInputBuffers);
#undef RESOLVE
}
else
return -1;
return 0;
}
#endif
static HANDLE open_device(const char *path, BOOL enumerate)
{
HANDLE handle;
DWORD desired_access = (enumerate)? 0: (GENERIC_WRITE | GENERIC_READ);
DWORD share_mode = (enumerate)?
FILE_SHARE_READ|FILE_SHARE_WRITE:
FILE_SHARE_READ;
handle = CreateFileA(path,
desired_access,
share_mode,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,/*FILE_ATTRIBUTE_NORMAL,*/
0);
return handle;
}
int HID_API_EXPORT hid_init(void)
{
#ifndef HIDAPI_USE_DDK
if (!initialized) {
if (lookup_functions() < 0) {
hid_exit();
return -1;
}
initialized = TRUE;
}
#endif
return 0;
}
int HID_API_EXPORT hid_exit(void)
{
#ifndef HIDAPI_USE_DDK
if (lib_handle)
FreeLibrary(lib_handle);
lib_handle = NULL;
initialized = FALSE;
#endif
return 0;
}
struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id)
{
BOOL res;
struct hid_device_info *root = NULL; /* return object */
struct hid_device_info *cur_dev = NULL;
/* Windows objects for interacting with the driver. */
GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, {0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30} };
SP_DEVINFO_DATA devinfo_data;
SP_DEVICE_INTERFACE_DATA device_interface_data;
SP_DEVICE_INTERFACE_DETAIL_DATA_A *device_interface_detail_data = NULL;
HDEVINFO device_info_set = INVALID_HANDLE_VALUE;
int device_index = 0;
int i;
if (hid_init() < 0)
return NULL;
/* Initialize the Windows objects. */
memset(&devinfo_data, 0x0, sizeof(devinfo_data));
devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA);
device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
/* Get information for all the devices belonging to the HID class. */
device_info_set = SetupDiGetClassDevsA(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
/* Iterate over each device in the HID class, looking for the right one. */
for (;;) {
HANDLE write_handle = INVALID_HANDLE_VALUE;
DWORD required_size = 0;
HIDD_ATTRIBUTES attrib;
res = SetupDiEnumDeviceInterfaces(device_info_set,
NULL,
&InterfaceClassGuid,
device_index,
&device_interface_data);
if (!res) {
/* A return of FALSE from this function means that
there are no more devices. */
break;
}
/* Call with 0-sized detail size, and let the function
tell us how long the detail struct needs to be. The
size is put in &required_size. */
res = SetupDiGetDeviceInterfaceDetailA(device_info_set,
&device_interface_data,
NULL,
0,
&required_size,
NULL);
/* Allocate a long enough structure for device_interface_detail_data. */
device_interface_detail_data = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*) malloc(required_size);
device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
/* Get the detailed data for this device. The detail data gives us
the device path for this device, which is then passed into
CreateFile() to get a handle to the device. */
res = SetupDiGetDeviceInterfaceDetailA(device_info_set,
&device_interface_data,
device_interface_detail_data,
required_size,
NULL,
NULL);
if (!res) {
/* register_error(dev, "Unable to call SetupDiGetDeviceInterfaceDetail");
Continue to the next device. */
goto cont;
}
/* Make sure this device is of Setup Class "HIDClass" and has a
driver bound to it. */
for (i = 0; ; i++) {
char driver_name[256];
/* Populate devinfo_data. This function will return failure
when there are no more interfaces left. */
res = SetupDiEnumDeviceInfo(device_info_set, i, &devinfo_data);
if (!res)
goto cont;
res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data,
SPDRP_CLASS, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL);
if (!res)
goto cont;
if (strcmp(driver_name, "HIDClass") == 0) {
/* See if there's a driver bound. */
res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data,
SPDRP_DRIVER, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL);
if (res)
break;
}
}
//wprintf(L"HandleName: %s\n", device_interface_detail_data->DevicePath);
/* Open a handle to the device */
write_handle = open_device(device_interface_detail_data->DevicePath, TRUE);
/* Check validity of write_handle. */
if (write_handle == INVALID_HANDLE_VALUE) {
/* Unable to open the device. */
//register_error(dev, "CreateFile");
goto cont_close;
}
/* Get the Vendor ID and Product ID for this device. */
attrib.Size = sizeof(HIDD_ATTRIBUTES);
HidD_GetAttributes(write_handle, &attrib);
//wprintf(L"Product/Vendor: %x %x\n", attrib.ProductID, attrib.VendorID);
/* Check the VID/PID to see if we should add this
device to the enumeration list. */
if ((vendor_id == 0x0 || attrib.VendorID == vendor_id) &&
(product_id == 0x0 || attrib.ProductID == product_id)) {
#define WSTR_LEN 512
const char *str;
struct hid_device_info *tmp;
PHIDP_PREPARSED_DATA pp_data = NULL;
HIDP_CAPS caps;
BOOLEAN res;
NTSTATUS nt_res;
wchar_t wstr[WSTR_LEN]; /* TODO: Determine Size */
size_t len;
/* VID/PID match. Create the record. */
tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info));
if (cur_dev) {
cur_dev->next = tmp;
}
else {
root = tmp;
}
cur_dev = tmp;
/* Get the Usage Page and Usage for this device. */
res = HidD_GetPreparsedData(write_handle, &pp_data);
if (res) {
nt_res = HidP_GetCaps(pp_data, &caps);
if (nt_res == HIDP_STATUS_SUCCESS) {
cur_dev->usage_page = caps.UsagePage;
cur_dev->usage = caps.Usage;
}
HidD_FreePreparsedData(pp_data);
}
/* Fill out the record */
cur_dev->next = NULL;
str = device_interface_detail_data->DevicePath;
if (str) {
len = strlen(str);
cur_dev->path = (char*) calloc(len+1, sizeof(char));
strncpy(cur_dev->path, str, len+1);
cur_dev->path[len] = '\0';
}
else
cur_dev->path = NULL;
/* Serial Number */
res = HidD_GetSerialNumberString(write_handle, wstr, sizeof(wstr));
wstr[WSTR_LEN-1] = 0x0000;
if (res) {
cur_dev->serial_number = _wcsdup(wstr);
}
/* Manufacturer String */
res = HidD_GetManufacturerString(write_handle, wstr, sizeof(wstr));
wstr[WSTR_LEN-1] = 0x0000;
if (res) {
cur_dev->manufacturer_string = _wcsdup(wstr);
}
/* Product String */
res = HidD_GetProductString(write_handle, wstr, sizeof(wstr));
wstr[WSTR_LEN-1] = 0x0000;
if (res) {
cur_dev->product_string = _wcsdup(wstr);
}
/* VID/PID */
cur_dev->vendor_id = attrib.VendorID;
cur_dev->product_id = attrib.ProductID;
/* Release Number */
cur_dev->release_number = attrib.VersionNumber;
/* Interface Number. It can sometimes be parsed out of the path
on Windows if a device has multiple interfaces. See
http://msdn.microsoft.com/en-us/windows/hardware/gg487473 or
search for "Hardware IDs for HID Devices" at MSDN. If it's not
in the path, it's set to -1. */
cur_dev->interface_number = -1;
if (cur_dev->path) {
char *interface_component = strstr(cur_dev->path, "&mi_");
if (interface_component) {
char *hex_str = interface_component + 4;
char *endptr = NULL;
cur_dev->interface_number = strtol(hex_str, &endptr, 16);
if (endptr == hex_str) {
/* The parsing failed. Set interface_number to -1. */
cur_dev->interface_number = -1;
}
}
}
}
cont_close:
CloseHandle(write_handle);
cont:
/* We no longer need the detail data. It can be freed */
free(device_interface_detail_data);
device_index++;
}
/* Close the device information handle. */
SetupDiDestroyDeviceInfoList(device_info_set);
return root;
}
void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs)
{
/* TODO: Merge this with the Linux version. This function is platform-independent. */
struct hid_device_info *d = devs;
while (d) {
struct hid_device_info *next = d->next;
free(d->path);
free(d->serial_number);
free(d->manufacturer_string);
free(d->product_string);
free(d);
d = next;
}
}
HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
{
/* TODO: Merge this functions with the Linux version. This function should be platform independent. */
struct hid_device_info *devs, *cur_dev;
const char *path_to_open = NULL;
hid_device *handle = NULL;
devs = hid_enumerate(vendor_id, product_id);
cur_dev = devs;
while (cur_dev) {
if (cur_dev->vendor_id == vendor_id &&
cur_dev->product_id == product_id) {
if (serial_number) {
if (wcscmp(serial_number, cur_dev->serial_number) == 0) {
path_to_open = cur_dev->path;
break;
}
}
else {
path_to_open = cur_dev->path;
break;
}
}
cur_dev = cur_dev->next;
}
if (path_to_open) {
/* Open the device */
handle = hid_open_path(path_to_open);
}
hid_free_enumeration(devs);
return handle;
}
HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path)
{
hid_device *dev;
HIDP_CAPS caps;
PHIDP_PREPARSED_DATA pp_data = NULL;
BOOLEAN res;
NTSTATUS nt_res;
if (hid_init() < 0) {
return NULL;
}
dev = new_hid_device();
/* Open a handle to the device */
dev->device_handle = open_device(path, FALSE);
/* Check validity of write_handle. */
if (dev->device_handle == INVALID_HANDLE_VALUE) {
/* Unable to open the device. */
register_error(dev, "CreateFile");
goto err;
}
/* Set the Input Report buffer size to 64 reports. */
res = HidD_SetNumInputBuffers(dev->device_handle, 64);
if (!res) {
register_error(dev, "HidD_SetNumInputBuffers");
goto err;
}
/* Get the Input Report length for the device. */
res = HidD_GetPreparsedData(dev->device_handle, &pp_data);
if (!res) {
register_error(dev, "HidD_GetPreparsedData");
goto err;
}
nt_res = HidP_GetCaps(pp_data, &caps);
if (nt_res != HIDP_STATUS_SUCCESS) {
register_error(dev, "HidP_GetCaps");
goto err_pp_data;
}
dev->output_report_length = caps.OutputReportByteLength;
dev->input_report_length = caps.InputReportByteLength;
HidD_FreePreparsedData(pp_data);
dev->read_buf = (char*) malloc(dev->input_report_length);
return dev;
err_pp_data:
HidD_FreePreparsedData(pp_data);
err:
free_hid_device(dev);
return NULL;
}
int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length)
{
DWORD bytes_written;
BOOL res;
OVERLAPPED ol;
unsigned char *buf;
memset(&ol, 0, sizeof(ol));
/* Make sure the right number of bytes are passed to WriteFile. Windows
expects the number of bytes which are in the _longest_ report (plus
one for the report number) bytes even if the data is a report
which is shorter than that. Windows gives us this value in
caps.OutputReportByteLength. If a user passes in fewer bytes than this,
create a temporary buffer which is the proper size. */
if (length >= dev->output_report_length) {
/* The user passed the right number of bytes. Use the buffer as-is. */
buf = (unsigned char *) data;
} else {
/* Create a temporary buffer and copy the user's data
into it, padding the rest with zeros. */
buf = (unsigned char *) malloc(dev->output_report_length);
memcpy(buf, data, length);
memset(buf + length, 0, dev->output_report_length - length);
length = dev->output_report_length;
}
res = WriteFile(dev->device_handle, buf, length, NULL, &ol);
if (!res) {
if (GetLastError() != ERROR_IO_PENDING) {
/* WriteFile() failed. Return error. */
register_error(dev, "WriteFile");
bytes_written = -1;
goto end_of_function;
}
}
/* Wait here until the write is done. This makes
hid_write() synchronous. */
res = GetOverlappedResult(dev->device_handle, &ol, &bytes_written, TRUE/*wait*/);
if (!res) {
/* The Write operation failed. */
register_error(dev, "WriteFile");
bytes_written = -1;
goto end_of_function;
}
end_of_function:
if (buf != data)
free(buf);
return bytes_written;
}
int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
{
DWORD bytes_read = 0;
size_t copy_len = 0;
BOOL res;
/* Copy the handle for convenience. */
HANDLE ev = dev->ol.hEvent;
if (!dev->read_pending) {
/* Start an Overlapped I/O read. */
dev->read_pending = TRUE;
memset(dev->read_buf, 0, dev->input_report_length);
ResetEvent(ev);
res = ReadFile(dev->device_handle, dev->read_buf, dev->input_report_length, &bytes_read, &dev->ol);
if (!res) {
if (GetLastError() != ERROR_IO_PENDING) {
/* ReadFile() has failed.
Clean up and return error. */
CancelIo(dev->device_handle);
dev->read_pending = FALSE;
goto end_of_function;
}
}
}
if (milliseconds >= 0) {
/* See if there is any data yet. */
res = WaitForSingleObject(ev, milliseconds);
if (res != WAIT_OBJECT_0) {
/* There was no data this time. Return zero bytes available,
but leave the Overlapped I/O running. */
return 0;
}
}
/* Either WaitForSingleObject() told us that ReadFile has completed, or
we are in non-blocking mode. Get the number of bytes read. The actual
data has been copied to the data[] array which was passed to ReadFile(). */
res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE/*wait*/);
/* Set pending back to false, even if GetOverlappedResult() returned error. */
dev->read_pending = FALSE;
if (res && bytes_read > 0) {
if (dev->read_buf[0] == 0x0) {
/* If report numbers aren't being used, but Windows sticks a report
number (0x0) on the beginning of the report anyway. To make this
work like the other platforms, and to make it work more like the
HID spec, we'll skip over this byte. */
bytes_read--;
copy_len = length > bytes_read ? bytes_read : length;
memcpy(data, dev->read_buf+1, copy_len);
}
else {
/* Copy the whole buffer, report number and all. */
copy_len = length > bytes_read ? bytes_read : length;
memcpy(data, dev->read_buf, copy_len);
}
}
end_of_function:
if (!res) {
register_error(dev, "GetOverlappedResult");
return -1;
}
return copy_len;
}
int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length)
{
return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0);
}
int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock)
{
dev->blocking = !nonblock;
return 0; /* Success */
}
int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length)
{
BOOL res = HidD_SetFeature(dev->device_handle, (PVOID)data, length);
if (!res) {
register_error(dev, "HidD_SetFeature");
return -1;
}
return length;
}
int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
{
BOOL res;
#if 0
res = HidD_GetFeature(dev->device_handle, data, length);
if (!res) {
register_error(dev, "HidD_GetFeature");
return -1;
}
return 0; /* HidD_GetFeature() doesn't give us an actual length, unfortunately */
#else
DWORD bytes_returned;
OVERLAPPED ol;
memset(&ol, 0, sizeof(ol));
res = DeviceIoControl(dev->device_handle,
IOCTL_HID_GET_FEATURE,
data, length,
data, length,
&bytes_returned, &ol);
if (!res) {
if (GetLastError() != ERROR_IO_PENDING) {
/* DeviceIoControl() failed. Return error. */
register_error(dev, "Send Feature Report DeviceIoControl");
return -1;
}
}
/* Wait here until the write is done. This makes
hid_get_feature_report() synchronous. */
res = GetOverlappedResult(dev->device_handle, &ol, &bytes_returned, TRUE/*wait*/);
if (!res) {
/* The operation failed. */
register_error(dev, "Send Feature Report GetOverLappedResult");
return -1;
}
/* bytes_returned does not include the first byte which contains the
report ID. The data buffer actually contains one more byte than
bytes_returned. */
bytes_returned++;
return bytes_returned;
#endif
}
void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev)
{
if (!dev)
return;
CancelIo(dev->device_handle);
free_hid_device(dev);
}
int HID_API_EXPORT_CALL HID_API_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen)
{
BOOL res;
res = HidD_GetManufacturerString(dev->device_handle, string, sizeof(wchar_t) * maxlen);
if (!res) {
register_error(dev, "HidD_GetManufacturerString");
return -1;
}
return 0;
}
int HID_API_EXPORT_CALL HID_API_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen)
{
BOOL res;
res = HidD_GetProductString(dev->device_handle, string, sizeof(wchar_t) * maxlen);
if (!res) {
register_error(dev, "HidD_GetProductString");
return -1;
}
return 0;
}
int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen)
{
BOOL res;
res = HidD_GetSerialNumberString(dev->device_handle, string, sizeof(wchar_t) * maxlen);
if (!res) {
register_error(dev, "HidD_GetSerialNumberString");
return -1;
}
return 0;
}
int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)
{
BOOL res;
res = HidD_GetIndexedString(dev->device_handle, string_index, string, sizeof(wchar_t) * maxlen);
if (!res) {
register_error(dev, "HidD_GetIndexedString");
return -1;
}
return 0;
}
HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev)
{
return (wchar_t*)dev->last_error_str;
}
/*#define PICPGM*/
/*#define S11*/
#define P32
#ifdef S11
unsigned short VendorID = 0xa0a0;
unsigned short ProductID = 0x0001;
#endif
#ifdef P32
unsigned short VendorID = 0x04d8;
unsigned short ProductID = 0x3f;
#endif
#ifdef PICPGM
unsigned short VendorID = 0x04d8;
unsigned short ProductID = 0x0033;
#endif
#if 0
int __cdecl main(int argc, char* argv[])
{
int res;
unsigned char buf[65];
UNREFERENCED_PARAMETER(argc);
UNREFERENCED_PARAMETER(argv);
/* Set up the command buffer. */
memset(buf,0x00,sizeof(buf));
buf[0] = 0;
buf[1] = 0x81;
/* Open the device. */
int handle = open(VendorID, ProductID, L"12345");
if (handle < 0)
printf("unable to open device\n");
/* Toggle LED (cmd 0x80) */
buf[1] = 0x80;
res = write(handle, buf, 65);
if (res < 0)
printf("Unable to write()\n");
/* Request state (cmd 0x81) */
buf[1] = 0x81;
write(handle, buf, 65);
if (res < 0)
printf("Unable to write() (2)\n");
/* Read requested state */
read(handle, buf, 65);
if (res < 0)
printf("Unable to read()\n");
/* Print out the returned buffer. */
for (int i = 0; i < 4; i++)
printf("buf[%d]: %d\n", i, buf[i]);
return 0;
}
#endif
#ifdef __cplusplus
} /* extern "C" */
#endif

View File

@ -1,387 +0,0 @@
/*******************************************************
HIDAPI - Multi-Platform library for
communication with HID devices.
Alan Ott
Signal 11 Software
8/22/2009
Copyright 2009, All Rights Reserved.
At the discretion of the user of this library,
this software may be licensed under the terms of the
GNU General Public License v3, a BSD-Style license, or the
original HIDAPI license as outlined in the LICENSE.txt,
LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
files located at the root of the source distribution.
These files may also be found in the public source
code repository located at:
http://github.com/signal11/hidapi .
********************************************************/
/** @file
* @defgroup API hidapi API
*/
#ifndef HIDAPI_H__
#define HIDAPI_H__
#include <wchar.h>
#ifdef _WIN32
#define HID_API_EXPORT __declspec(dllexport)
#define HID_API_CALL
#else
#define HID_API_EXPORT /**< API export macro */
#define HID_API_CALL /**< API call macro */
#endif
#define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/
#ifdef __cplusplus
extern "C" {
#endif
struct hid_device_;
typedef struct hid_device_ hid_device; /**< opaque hidapi structure */
/** hidapi info structure */
struct hid_device_info {
/** Platform-specific device path */
char *path;
/** Device Vendor ID */
unsigned short vendor_id;
/** Device Product ID */
unsigned short product_id;
/** Serial Number */
wchar_t *serial_number;
/** Device Release Number in binary-coded decimal,
also known as Device Version Number */
unsigned short release_number;
/** Manufacturer String */
wchar_t *manufacturer_string;
/** Product string */
wchar_t *product_string;
/** Usage Page for this Device/Interface
(Windows/Mac only). */
unsigned short usage_page;
/** Usage for this Device/Interface
(Windows/Mac only).*/
unsigned short usage;
/** The USB interface which this logical device
represents. Valid on both Linux implementations
in all cases, and valid on the Windows implementation
only if the device contains more than one interface. */
int interface_number;
/** Pointer to the next device */
struct hid_device_info *next;
};
/** @brief Initialize the HIDAPI library.
This function initializes the HIDAPI library. Calling it is not
strictly necessary, as it will be called automatically by
hid_enumerate() and any of the hid_open_*() functions if it is
needed. This function should be called at the beginning of
execution however, if there is a chance of HIDAPI handles
being opened by different threads simultaneously.
@ingroup API
@returns
This function returns 0 on success and -1 on error.
*/
int HID_API_EXPORT HID_API_CALL hid_init(void);
/** @brief Finalize the HIDAPI library.
This function frees all of the static data associated with
HIDAPI. It should be called at the end of execution to avoid
memory leaks.
@ingroup API
@returns
This function returns 0 on success and -1 on error.
*/
int HID_API_EXPORT HID_API_CALL hid_exit(void);
/** @brief Enumerate the HID Devices.
This function returns a linked list of all the HID devices
attached to the system which match vendor_id and product_id.
If @p vendor_id is set to 0 then any vendor matches.
If @p product_id is set to 0 then any product matches.
If @p vendor_id and @p product_id are both set to 0, then
all HID devices will be returned.
@ingroup API
@param vendor_id The Vendor ID (VID) of the types of device
to open.
@param product_id The Product ID (PID) of the types of
device to open.
@returns
This function returns a pointer to a linked list of type
struct #hid_device, containing information about the HID devices
attached to the system, or NULL in the case of failure. Free
this linked list by calling hid_free_enumeration().
*/
struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id);
/** @brief Free an enumeration Linked List
This function frees a linked list created by hid_enumerate().
@ingroup API
@param devs Pointer to a list of struct_device returned from
hid_enumerate().
*/
void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs);
/** @brief Open a HID device using a Vendor ID (VID), Product ID
(PID) and optionally a serial number.
If @p serial_number is NULL, the first device with the
specified VID and PID is opened.
@ingroup API
@param vendor_id The Vendor ID (VID) of the device to open.
@param product_id The Product ID (PID) of the device to open.
@param serial_number The Serial Number of the device to open
(Optionally NULL).
@returns
This function returns a pointer to a #hid_device object on
success or NULL on failure.
*/
HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number);
/** @brief Open a HID device by its path name.
The path name be determined by calling hid_enumerate(), or a
platform-specific path name can be used (eg: /dev/hidraw0 on
Linux).
@ingroup API
@param path The path name of the device to open
@returns
This function returns a pointer to a #hid_device object on
success or NULL on failure.
*/
HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path);
/** @brief Write an Output report to a HID device.
The first byte of @p data[] must contain the Report ID. For
devices which only support a single report, this must be set
to 0x0. The remaining bytes contain the report data. Since
the Report ID is mandatory, calls to hid_write() will always
contain one more byte than the report contains. For example,
if a hid report is 16 bytes long, 17 bytes must be passed to
hid_write(), the Report ID (or 0x0, for devices with a
single report), followed by the report data (16 bytes). In
this example, the length passed in would be 17.
hid_write() will send the data on the first OUT endpoint, if
one exists. If it does not, it will send the data through
the Control Endpoint (Endpoint 0).
@ingroup API
@param device A device handle returned from hid_open().
@param data The data to send, including the report number as
the first byte.
@param length The length in bytes of the data to send.
@returns
This function returns the actual number of bytes written and
-1 on error.
*/
int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length);
/** @brief Read an Input report from a HID device with timeout.
Input reports are returned
to the host through the INTERRUPT IN endpoint. The first byte will
contain the Report number if the device uses numbered reports.
@ingroup API
@param device A device handle returned from hid_open().
@param data A buffer to put the read data into.
@param length The number of bytes to read. For devices with
multiple reports, make sure to read an extra byte for
the report number.
@param milliseconds timeout in milliseconds or -1 for blocking wait.
@returns
This function returns the actual number of bytes read and
-1 on error. If no packet was available to be read within
the timeout period, this function returns 0.
*/
int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds);
/** @brief Read an Input report from a HID device.
Input reports are returned
to the host through the INTERRUPT IN endpoint. The first byte will
contain the Report number if the device uses numbered reports.
@ingroup API
@param device A device handle returned from hid_open().
@param data A buffer to put the read data into.
@param length The number of bytes to read. For devices with
multiple reports, make sure to read an extra byte for
the report number.
@returns
This function returns the actual number of bytes read and
-1 on error. If no packet was available to be read and
the handle is in non-blocking mode, this function returns 0.
*/
int HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length);
/** @brief Set the device handle to be non-blocking.
In non-blocking mode calls to hid_read() will return
immediately with a value of 0 if there is no data to be
read. In blocking mode, hid_read() will wait (block) until
there is data to read before returning.
Nonblocking can be turned on and off at any time.
@ingroup API
@param device A device handle returned from hid_open().
@param nonblock enable or not the nonblocking reads
- 1 to enable nonblocking
- 0 to disable nonblocking.
@returns
This function returns 0 on success and -1 on error.
*/
int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock);
/** @brief Send a Feature report to the device.
Feature reports are sent over the Control endpoint as a
Set_Report transfer. The first byte of @p data[] must
contain the Report ID. For devices which only support a
single report, this must be set to 0x0. The remaining bytes
contain the report data. Since the Report ID is mandatory,
calls to hid_send_feature_report() will always contain one
more byte than the report contains. For example, if a hid
report is 16 bytes long, 17 bytes must be passed to
hid_send_feature_report(): the Report ID (or 0x0, for
devices which do not use numbered reports), followed by the
report data (16 bytes). In this example, the length passed
in would be 17.
@ingroup API
@param device A device handle returned from hid_open().
@param data The data to send, including the report number as
the first byte.
@param length The length in bytes of the data to send, including
the report number.
@returns
This function returns the actual number of bytes written and
-1 on error.
*/
int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length);
/** @brief Get a feature report from a HID device.
Make sure to set the first byte of @p data[] to the Report
ID of the report to be read. Make sure to allow space for
this extra byte in @p data[].
@ingroup API
@param device A device handle returned from hid_open().
@param data A buffer to put the read data into, including
the Report ID. Set the first byte of @p data[] to the
Report ID of the report to be read.
@param length The number of bytes to read, including an
extra byte for the report ID. The buffer can be longer
than the actual report.
@returns
This function returns the number of bytes read and
-1 on error.
*/
int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length);
/** @brief Close a HID device.
@ingroup API
@param device A device handle returned from hid_open().
*/
void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device);
/** @brief Get The Manufacturer String from a HID device.
@ingroup API
@param device A device handle returned from hid_open().
@param string A wide string buffer to put the data into.
@param maxlen The length of the buffer in multiples of wchar_t.
@returns
This function returns 0 on success and -1 on error.
*/
int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen);
/** @brief Get The Product String from a HID device.
@ingroup API
@param device A device handle returned from hid_open().
@param string A wide string buffer to put the data into.
@param maxlen The length of the buffer in multiples of wchar_t.
@returns
This function returns 0 on success and -1 on error.
*/
int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen);
/** @brief Get The Serial Number String from a HID device.
@ingroup API
@param device A device handle returned from hid_open().
@param string A wide string buffer to put the data into.
@param maxlen The length of the buffer in multiples of wchar_t.
@returns
This function returns 0 on success and -1 on error.
*/
int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen);
/** @brief Get a string from a HID device, based on its string index.
@ingroup API
@param device A device handle returned from hid_open().
@param string_index The index of the string to get.
@param string A wide string buffer to put the data into.
@param maxlen The length of the buffer in multiples of wchar_t.
@returns
This function returns 0 on success and -1 on error.
*/
int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen);
/** @brief Get a string describing the last error which occurred.
@ingroup API
@param device A device handle returned from hid_open().
@returns
This function returns a string containing the last error
which occurred or NULL if none has occurred.
*/
HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device);
#ifdef __cplusplus
}
#endif
#endif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@ -1,159 +0,0 @@
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include "hexdump.h"
static unsigned char chk(unsigned char *buf, int len)
{
int i;
unsigned char r = 0;
for (i=0; i<len; i++) {
r += buf[i];
}
return r;
}
/* \return The highest address written to, or negative on errors.
*/
int load_ihex(const char *file, unsigned char *dstbuf, int bufsize)
{
FILE *fptr;
char linebuf[550];
unsigned char databuf[2+1+255+1];
int ret = 0;
int line = 0;
int eof_seen = 0;
unsigned int max_address = 0;
unsigned int offset = 0;
fptr = fopen(file, "r");
if (!fptr) {
perror("fopen");
return -1;
}
do {
if (fgets(linebuf, sizeof(linebuf), fptr)) {
unsigned int data_count;
unsigned int address;
int input_nibbles, input_bytes;
int i;
line++;
if (linebuf[0] != ':') {
fprintf(stderr, "Ignored invalid line %d\n", line);
continue;
}
if (eof_seen) {
fprintf(stderr, "extra data after EOF record in hex file\n");
ret = -7;
goto err;
}
// :10 0000 00 92C064C7ABC0AAC0A9C0A8C0A7C0A6C0 00
// ^ ^ ^ ^ ^-- Checksum
// | | | +----- Data [data_count]
// | | +---- Record type
// | +------ Address
// +----- data_count
//
input_nibbles = strlen(linebuf) - 1;
for (input_bytes=0,i=0; i<input_nibbles; i+=2) {
unsigned int byte;
if (1 != sscanf(linebuf + 1 + i, "%02x", &byte)) {
break;
}
databuf[input_bytes] = byte;
input_bytes++;
}
//printf("Input bytes: %d\n", input_bytes);
//printHexBuf(databuf, input_bytes);
// Validate the record checksum
if (chk(databuf, input_bytes)) {
fprintf(stderr, "Bad checksum at line %d\n", line);
ret = -4;
goto err;
}
// Data length sanity check
data_count = databuf[0];
if (input_bytes != 1+2+1+data_count+1) {
fprintf(stderr, "Invalid record (less data than expected) at line %d\n", line);
ret = -5;
goto err;
}
address = databuf[1]<<8 | databuf[2];
switch(databuf[3])
{
case 0x00: // Data
if (address + offset + data_count > bufsize) {
fprintf(stderr, "hex file too large\n");
ret = -6;
goto err;
}
if (address + offset + data_count > max_address) {
max_address = address + offset + data_count;
}
memcpy(dstbuf + address + offset, databuf + 4, data_count);
break;
case 0x01: // EOF
eof_seen = 1;
break;
case 0x04: // Extended linear address
if (data_count != 2) {
fprintf(stderr, "ihex parser: Malformatted 0x04 record at line %d\n", line);
ret = -8;
goto err;
}
offset = (databuf[4] << 24) | (databuf[5] << 16);
//printf("OFfset: 0x%08x\n", offset);
break;
case 0x03: // Start segment address
case 0x05: // Start linear address
// Ignored
break;
default:
case 0x02: // Extended segment address
fprintf(stderr, "ihex parser: Unimplemented record type 0x%02x at line %d\n", databuf[3], line);
ret = -2;
goto err;
}
}
} while (!feof(fptr));
fclose(fptr);
return max_address;
err:
fclose(fptr);
return ret;
}

View File

@ -1,8 +0,0 @@
#ifndef _ihex_h__
#define _ihex_h__
/* \return File size, or negative value on error.*/
int load_ihex(const char *file, unsigned char *dstbuf, int bufsize);
#endif // _ihex_h__

View File

@ -1 +0,0 @@
*.exe

View File

@ -1,614 +0,0 @@
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include <stdlib.h>
#include <unistd.h>
#include <wchar.h>
#include "hexdump.h"
#include "version.h"
#include "gcn64.h"
#include "gcn64lib.h"
#include "gc2n64_adapter.h"
#include "mempak.h"
#include "mempak_gcn64usb.h"
#include "../requests.h"
#include "../gcn64_protocol.h"
static void printUsage(void)
{
printf("./gcn64_ctl [OPTION]... [COMMAND]....\n");
printf("Control tool for GC/N64 to USB adapter. Version %s\n", VERSION_STR);
printf("\n");
printf("Options:\n");
printf(" -h, --help Print help\n");
printf(" -l, --list List devices\n");
printf(" -s serial Operate on specified device (required unless -f is specified)\n");
printf(" -f, --force If no serial is specified, use first device detected.\n");
printf(" -o, --outfile file Output file for read operations (eg: --n64-mempak-dump)\n");
//printf(" -i, --infile file Input file for write operations (eg: --gc_to_n64_update)\n");
printf(" --nonstop Continue testing forever or until an error occurs.\n");
printf("\n");
printf("Configuration commands:\n");
printf(" --get_version Read adapter firmware version\n");
printf(" --set_serial serial Assign a new device serial number\n");
printf(" --get_serial Read serial from eeprom\n");
printf(" --set_poll_rate ms Set time between controller polls in milliseconds\n");
printf(" --get_poll_rate Read configured poll rate\n");
printf(" --get_controller_type Display the type of controller currently connected\n");
printf("\n");
printf("Advanced commands:\n");
printf(" --bootloader Re-enumerate in bootloader mode\n");
printf(" --suspend_polling Stop polling the controller\n");
printf(" --resume_polling Re-start polling the controller\n");
printf(" --get_signature Get the firmware signature\n");
printf("\n");
printf("Raw controller commands:\n");
printf(" --n64_getstatus Read N64 controller status now\n");
printf(" --gc_getstatus Read GC controller status now (turns rumble OFF)\n");
printf(" --gc_getstatus_rumble Read GC controller status now (turns rumble ON)\n");
printf(" --n64_getcaps Get N64 controller capabilities (or status such as pak present)\n");
printf(" --n64_mempak_dump Dump N64 mempak contents (Use with --outfile to write to file)\n");
printf(" --n64_mempak_write file Write file to N64 mempak\n");
printf("\n");
printf("GC to N64 adapter commands: (For GC to N64 adapter connected to GC/N64 to USB adapter)\n");
printf(" --gc_to_n64_info Display info on adapter (version, config, etc)\n");
printf(" --gc_to_n64_update file.hex Update GC to N64 adapter firmware\n");
printf(" --gc_to_n64_read_mapping id Dump a mapping (Use with --outfile to write to file)\n");
printf(" --gc_to_n64_load_mapping file Load a mapping from a file and send it to the adapter\n");
printf(" --gc_to_n64_store_current_mapping slot Store the current mapping to one of the D-Pad slots.\n");
printf("\n");
printf("GC to N64 adapter, development/debug commands:\n");
printf(" --gc_to_n64_echotest Perform a communication test (usable with --nonstop)\n");
printf(" --gc_to_n64_dump Display the firmware content in hex.\n");
printf(" --gc_to_n64_enter_bootloader Jump to the bootloader.\n");
printf(" --gc_to_n64_boot_application Exit bootloader and start application.\n");
printf("\n");
printf("Development/Experimental/Research commands: (use at your own risk)\n");
printf(" --si_8bit_scan Try all possible 1-byte commands, to see which one a controller responds to.\n");
printf(" --si_16bit_scan Try all possible 2-byte commands, to see which one a controller responds to.\n");
}
#define OPT_OUTFILE 'o'
#define OPT_INFILE 'i'
#define OPT_SET_SERIAL 257
#define OPT_GET_SERIAL 258
#define OPT_BOOTLOADER 300
#define OPT_N64_GETSTATUS 301
#define OPT_GC_GETSTATUS 302
#define OPT_GC_GETSTATUS_RUMBLE 303
#define OPT_N64_MEMPAK_DUMP 304
#define OPT_N64_GETCAPS 305
#define OPT_SUSPEND_POLLING 306
#define OPT_RESUME_POLLING 307
#define OPT_SET_POLL_INTERVAL 308
#define OPT_GET_POLL_INTERVAL 309
#define OPT_N64_MEMPAK_WRITE 310
#define OPT_SI8BIT_SCAN 311
#define OPT_SI16BIT_SCAN 312
#define OPT_GC_TO_N64_INFO 313
#define OPT_GC_TO_N64_TEST 314
#define OPT_GC_TO_N64_UPDATE 315
#define OPT_GC_TO_N64_DUMP 316
#define OPT_GC_TO_N64_ENTER_BOOTLOADER 317
#define OPT_GC_TO_N64_BOOT_APPLICATION 318
#define OPT_NONSTOP 319
#define OPT_GC_TO_N64_READ_MAPPING 320
#define OPT_GC_TO_N64_LOAD_MAPPING 321
#define OPT_GC_TO_N64_STORE_CURRENT_MAPPING 322
#define OPT_GET_VERSION 323
#define OPT_GET_SIGNATURE 324
#define OPT_GET_CTLTYPE 325
struct option longopts[] = {
{ "help", 0, NULL, 'h' },
{ "list", 0, NULL, 'l' },
{ "force", 0, NULL, 'f' },
{ "set_serial", 1, NULL, OPT_SET_SERIAL },
{ "get_serial", 0, NULL, OPT_GET_SERIAL },
{ "bootloader", 0, NULL, OPT_BOOTLOADER },
{ "n64_getstatus", 0, NULL, OPT_N64_GETSTATUS },
{ "gc_getstatus", 0, NULL, OPT_GC_GETSTATUS },
{ "gc_getstatus_rumble", 0, NULL, OPT_GC_GETSTATUS_RUMBLE },
{ "n64_getcaps", 0, NULL, OPT_N64_GETCAPS },
{ "n64_mempak_dump", 0, NULL, OPT_N64_MEMPAK_DUMP },
{ "suspend_polling", 0, NULL, OPT_SUSPEND_POLLING },
{ "resume_polling", 0, NULL, OPT_RESUME_POLLING },
{ "outfile", 1, NULL, OPT_OUTFILE },
{ "infile", 1, NULL, OPT_INFILE },
{ "set_poll_rate", 1, NULL, OPT_SET_POLL_INTERVAL },
{ "get_poll_rate", 0, NULL, OPT_GET_POLL_INTERVAL },
{ "n64_mempak_write", 1, NULL, OPT_N64_MEMPAK_WRITE },
{ "si_8bit_scan", 0, NULL, OPT_SI8BIT_SCAN },
{ "si_16bit_scan", 0, NULL, OPT_SI16BIT_SCAN },
{ "gc_to_n64_info", 0, NULL, OPT_GC_TO_N64_INFO },
{ "gc_to_n64_echotest", 0, NULL, OPT_GC_TO_N64_TEST },
{ "gc_to_n64_update", 1, NULL, OPT_GC_TO_N64_UPDATE },
{ "gc_to_n64_dump", 0, NULL, OPT_GC_TO_N64_DUMP },
{ "gc_to_n64_enter_bootloader", 0, NULL, OPT_GC_TO_N64_ENTER_BOOTLOADER },
{ "gc_to_n64_boot_application", 0, NULL, OPT_GC_TO_N64_BOOT_APPLICATION },
{ "gc_to_n64_read_mapping", 1, NULL, OPT_GC_TO_N64_READ_MAPPING },
{ "gc_to_n64_load_mapping", 1, NULL, OPT_GC_TO_N64_LOAD_MAPPING },
{ "gc_to_n64_store_current_mapping", 1, NULL, OPT_GC_TO_N64_STORE_CURRENT_MAPPING },
{ "nonstop", 0, NULL, OPT_NONSTOP },
{ "get_version", 0, NULL, OPT_GET_VERSION },
{ "get_signature", 0, NULL, OPT_GET_SIGNATURE },
{ "get_controller_type", 0, NULL, OPT_GET_CTLTYPE },
{ },
};
static int mempak_progress_cb(int addr, void *ctx)
{
printf("\r%s 0x%04x / 0x%04x ", (char*)ctx, addr, MEMPAK_MEM_SIZE); fflush(stdout);
return 0;
}
static int listDevices(void)
{
int n_found = 0;
struct gcn64_list_ctx *listctx;
struct gcn64_info inf;
listctx = gcn64_allocListCtx();
if (!listctx) {
fprintf(stderr, "List context could not be allocated\n");
return -1;
}
while (gcn64_listDevices(&inf, listctx))
{
n_found++;
printf("Found device '%ls', serial '%ls'\n", inf.str_prodname, inf.str_serial);
}
gcn64_freeListCtx(listctx);
printf("%d device(s) found\n", n_found);
return n_found;
}
int main(int argc, char **argv)
{
gcn64_hdl_t hdl;
struct gcn64_list_ctx *listctx;
int opt, retval = 0;
struct gcn64_info inf;
struct gcn64_info *selected_device = NULL;
int verbose = 0, use_first = 0, serial_specified = 0;
int nonstop = 0;
int cmd_list = 0;
#define TARGET_SERIAL_CHARS 128
wchar_t target_serial[TARGET_SERIAL_CHARS];
const char *short_optstr = "hls:vfo:";
const char *outfile = NULL;
const char *infile = NULL;
int gc2n64_channel = 0;
while((opt = getopt_long(argc, argv, short_optstr, longopts, NULL)) != -1) {
switch(opt)
{
case 's':
{
mbstate_t ps;
memset(&ps, 0, sizeof(ps));
if (mbsrtowcs(target_serial, (const char **)&optarg, TARGET_SERIAL_CHARS, &ps) < 1) {
fprintf(stderr, "Invalid serial number specified\n");
return -1;
}
serial_specified = 1;
}
break;
case 'f':
use_first = 1;
break;
case 'v':
verbose = 1;
break;
case 'h':
printUsage();
return 0;
case 'l':
cmd_list = 1;
break;
case 'o':
outfile = optarg;
printf("Output file: %s\n", outfile);
break;
case 'i':
infile = optarg;
printf("Input file: %s\n", infile);
break;
case OPT_NONSTOP:
nonstop = 1;
break;
case '?':
fprintf(stderr, "Unrecognized argument. Try -h\n");
return -1;
}
}
gcn64_init(verbose);
if (cmd_list) {
printf("Simply listing the devices...\n");
return listDevices();
}
if (!serial_specified && !use_first) {
fprintf(stderr, "A serial number or -f must be used. Try -h for more information.\n");
return 1;
}
listctx = gcn64_allocListCtx();
while ((selected_device = gcn64_listDevices(&inf, listctx)))
{
if (serial_specified) {
if (0 == wcscmp(inf.str_serial, target_serial)) {
break;
}
}
else {
// use_first == 1
printf("Will use device '%ls' serial '%ls'\n", inf.str_prodname, inf.str_serial);
break;
}
}
gcn64_freeListCtx(listctx);
if (!selected_device) {
if (serial_specified) {
fprintf(stderr, "Device not found\n");
} else {
fprintf(stderr, "No device found\n");
}
return 1;
}
hdl = gcn64_openDevice(selected_device);
if (!hdl) {
printf("Error opening device. (Do you have permissions?)\n");
return 1;
}
optind = 1;
while((opt = getopt_long(argc, argv, short_optstr, longopts, NULL)) != -1)
{
unsigned char cmd[64] = { };
int n;
int cmdlen = 0;
int do_exchange = 0;
switch (opt)
{
case OPT_SET_POLL_INTERVAL:
cmd[0] = atoi(optarg);
printf("Setting poll interval to %d ms\n", cmd[0]);
gcn64lib_setConfig(hdl, CFG_PARAM_POLL_INTERVAL0, cmd, 1);
break;
case OPT_GET_POLL_INTERVAL:
n = gcn64lib_getConfig(hdl, CFG_PARAM_POLL_INTERVAL0, cmd, sizeof(cmd));
if (n == 1) {
printf("Poll interval: %d ms\n", cmd[0]);
}
break;
case OPT_SET_SERIAL:
printf("Setting serial...");
if (strlen(optarg) != 6) {
fprintf(stderr, "Serial number must be 6 characters\n");
return -1;
}
gcn64lib_setConfig(hdl, CFG_PARAM_SERIAL, (void*)optarg, 6);
break;
case OPT_GET_SERIAL:
n = gcn64lib_getConfig(hdl, CFG_PARAM_SERIAL, cmd, sizeof(cmd));
if (n==6) {
cmd[6] = 0;
printf("Serial: %s\n", cmd);
}
break;
case OPT_BOOTLOADER:
printf("Sending 'jump to bootloader' command...");
gcn64lib_bootloader(hdl);
break;
case OPT_SUSPEND_POLLING:
gcn64lib_suspendPolling(hdl, 1);
break;
case OPT_RESUME_POLLING:
gcn64lib_suspendPolling(hdl, 0);
break;
case OPT_N64_GETSTATUS:
cmd[0] = N64_GET_STATUS;
n = gcn64lib_rawSiCommand(hdl, 0, cmd, 1, cmd, sizeof(cmd));
if (n >= 0) {
printf("N64 Get status[%d]: ", n);
printHexBuf(cmd, n);
}
break;
case OPT_GC_GETSTATUS_RUMBLE:
case OPT_GC_GETSTATUS:
cmd[0] = GC_GETSTATUS1;
cmd[1] = GC_GETSTATUS2;
cmd[2] = GC_GETSTATUS3(opt == OPT_GC_GETSTATUS_RUMBLE);
n = gcn64lib_rawSiCommand(hdl, 0, cmd, 3, cmd, sizeof(cmd));
if (n >= 0) {
printf("GC Get status[%d]: ", n);
printHexBuf(cmd, n);
}
break;
case OPT_N64_GETCAPS:
cmd[0] = N64_GET_CAPABILITIES;
//cmd[0] = 0xff;
n = gcn64lib_rawSiCommand(hdl, 0, cmd, 1, cmd, sizeof(cmd));
if (n >= 0) {
printf("N64 Get caps[%d]: ", n);
printHexBuf(cmd, n);
}
break;
case OPT_N64_MEMPAK_DUMP:
{
mempak_structure_t *pak;
int res;
printf("Reading mempak...\n");
res = gcn64lib_mempak_download(hdl, 0, &pak, mempak_progress_cb, "Reading address");
printf("\n");
switch (res)
{
case 0:
if (outfile) {
int file_format;
file_format = mempak_getFilenameFormat(outfile);
if (file_format == MPK_FORMAT_INVALID) {
fprintf(stderr, "Unknown file format (neither .MPK nor .N64). Not saving.\n");
} else {
if (0 == mempak_saveToFile(pak, outfile, file_format)) {
printf("Wrote file '%s' in %s format\n", outfile, mempak_format2string(file_format));
} else {
fprintf(stderr, "error writing file\n");
}
}
} else { // No outfile
mempak_hexdump(pak);
}
mempak_free(pak);
break;
case -1:
fprintf(stderr, "No mempak detected\n");
break;
case -2:
fprintf(stderr, "I/O error reading pak\n");
break;
default:
case -3:
fprintf(stderr, "Error\n");
break;
}
}
break;
case OPT_N64_MEMPAK_WRITE:
{
mempak_structure_t *pak;
int res;
printf("Input file: %s\n", optarg);
pak = mempak_loadFromFile(optarg);
if (!pak) {
fprintf(stderr, "Failed to load mempak\n");
return -1;
}
printf("Writing to mempak...\n");
res = gcn64lib_mempak_upload(hdl, 0, pak, mempak_progress_cb, "Writing address");
printf("\n");
if (res) {
switch(res)
{
case -1:
fprintf(stderr, "Error: No mempak detected.\n");
break;
case -2:
fprintf(stderr, "I/O error writing to pak.\n");
break;
default:
fprintf(stderr, "Error uploading mempak\n");
}
} else {
printf("Mempak uploaded\n");
}
mempak_free(pak);
}
break;
case OPT_SI8BIT_SCAN:
gcn64lib_8bit_scan(hdl, 0, 255);
break;
case OPT_SI16BIT_SCAN:
gcn64lib_16bit_scan(hdl, 0, 0xffff);
break;
case OPT_GC_TO_N64_INFO:
{
struct gc2n64_adapter_info inf;
gc2n64_adapter_getInfo(hdl, gc2n64_channel, &inf);
gc2n64_adapter_printInfo(&inf);
}
break;
case OPT_GC_TO_N64_TEST:
{
int i=0;
do {
n = gc2n64_adapter_echotest(hdl, gc2n64_channel, 1);
if (n != 0) {
printf("Test failed\n");
return -1;
}
usleep(1000 * (i));
i++;
if (i>100)
i=0;
printf("."); fflush(stdout);
} while (nonstop);
printf("Test ok\n");
}
break;
case OPT_GC_TO_N64_UPDATE:
gc2n64_adapter_updateFirmware(hdl, gc2n64_channel, optarg);
break;
case OPT_GC_TO_N64_DUMP:
gc2n64_adapter_dumpFlash(hdl, gc2n64_channel);
break;
case OPT_GC_TO_N64_ENTER_BOOTLOADER:
gc2n64_adapter_enterBootloader(hdl, gc2n64_channel);
gc2n64_adapter_waitForBootloader(hdl, gc2n64_channel, 5);
break;
case OPT_GC_TO_N64_BOOT_APPLICATION:
gc2n64_adapter_bootApplication(hdl, gc2n64_channel);
break;
case OPT_GC_TO_N64_READ_MAPPING:
{
struct gc2n64_adapter_info inf;
int map_id;
map_id = atoi(optarg);
if ((map_id <= 0) || (map_id > GC2N64_NUM_MAPPINGS)) {
fprintf(stderr, "Invalid mapping id (1 to 4)\n");
return -1;
}
gc2n64_adapter_getInfo(hdl, gc2n64_channel, &inf);
printf("Mapping %d : { ", map_id);
gc2n64_adapter_printMapping(&inf.app.mappings[map_id-1]);
printf(" }\n");
if (outfile) {
printf("Writing mapping to file '%s'\n", outfile);
gc2n64_adapter_saveMapping(&inf.app.mappings[map_id-1], outfile);
}
}
break;
case OPT_GC_TO_N64_LOAD_MAPPING:
{
struct gc2n64_adapter_mapping *mapping;
printf("Reading mapping from file '%s'\n", optarg);
mapping = gc2n64_adapter_loadMapping(optarg);
if (!mapping) {
fprintf(stderr, "Failed to load mapping\n");
return -1;
}
printf("Mapping : { ");
gc2n64_adapter_printMapping(mapping);
printf(" }\n");
gc2n64_adapter_setMapping(hdl, gc2n64_channel, mapping);
free(mapping);
}
break;
case OPT_GC_TO_N64_STORE_CURRENT_MAPPING:
{
int slot;
slot = atoi(optarg);
if (slot < 1 || slot > 4) {
fprintf(stderr, "Mapping out of range (1-4)\n");
return -1;
}
if (0 == gc2n64_adapter_storeCurrentMapping(hdl, gc2n64_channel, slot)) {
printf("Stored mapping to slot %d (%s)\n", slot, gc2n64_adapter_getMappingSlotName(slot, 0));
} else {
printf("Error storing mapping\n");
}
}
break;
case OPT_GET_VERSION:
{
char version[64];
if (0 == gcn64lib_getVersion(hdl, version, sizeof(version))) {
printf("Firmware version: %s\n", version);
}
}
break;
case OPT_GET_SIGNATURE:
{
char sig[64];
if (0 == gcn64lib_getSignature(hdl, sig, sizeof(sig))) {
printf("Signature: %s\n", sig);
}
}
break;
case OPT_GET_CTLTYPE:
{
int type;
type = gcn64lib_getControllerType(hdl, 0);
printf("Controller type 0x%02x: %s\n", type, gcn64lib_controllerName(type));
}
break;
}
if (do_exchange) {
int i;
n = gcn64_exchange(hdl, cmd, cmdlen, cmd, sizeof(cmd));
if (n<0)
break;
printf("Result: %d bytes: ", n);
for (i=0; i<n; i++) {
printf("%02x ", cmd[i]);
}
printf("\n");
}
}
gcn64_closeDevice(hdl);
gcn64_shutdown();
return retval;
}

View File

@ -1,31 +0,0 @@
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
void *memmem(const void *haystack, size_t haystack_len,
const void *needle, size_t needle_len)
{
int i;
if (needle_len > haystack_len)
return NULL;
for (i=0; i<haystack_len; i++) {
if (!memcmp(haystack +i, needle, needle_len))
return haystack + i;
}
return NULL;
}

View File

@ -1,472 +0,0 @@
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define _GNU_SOURCE // for strcasestr
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <libgen.h>
#include "mempak.h"
#define DEXDRIVE_DATA_OFFSET 0x1040
#define DEXDRIVE_COMMENT_OFFSET 0x40
mempak_structure_t *mempak_new(void)
{
mempak_structure_t *mpk;
mpk = calloc(1, sizeof(mempak_structure_t));
if (!mpk) {
perror("calloc");
return NULL;
}
format_mempak(mpk);
return mpk;
}
static int mempak_findFreeNote(mempak_structure_t *mpk, entry_structure_t *entry_data, int *note_id)
{
int i;
if (!mpk)
return -1;
if (!entry_data)
return -1;
for (i=0; i<MEMPAK_NUM_NOTES; i++) {
if (0 != get_mempak_entry(mpk, i, entry_data)) {
return -1;
}
if (!entry_data->valid) {
if (note_id) {
*note_id = i;
}
return 0;
}
}
return -1;
}
/**
* \param mpk The memory pack to operate on
* \param notefile The filename of the note to load
* \param dst_note_id 0-15: (Over)write to specific note, -1: auto (first free)
* \param note_id Stores the id of the note that was used
* \return -1: Error, -2: Not enough space in mempak
*/
int mempak_importNote(mempak_structure_t *mpk, const char *notefile, int dst_note_id, int *note_id)
{
int free_blocks = get_mempak_free_space(mpk);
FILE *fptr;
unsigned char entry_data[32];
unsigned char *data = NULL;
entry_structure_t entry;
int res;
if (dst_note_id < -1 || dst_note_id > 15) {
fprintf(stderr, "Out of bound dst_note_id\n");
return -1;
}
printf("Current free blocks: %d\n", free_blocks);
fptr = fopen(notefile, "rb");
if (!fptr) {
perror("fopen");
return -1;
}
if (1 != fread(entry_data, 32, 1, fptr)) {
perror("fread");
fclose(fptr);
return -1;
}
/* I follow the same convention as bryc's javascript mempak editor[1]
* by looking for an inode number of 0xCAFE.
*
* [1] https://github.com/bryc/mempak
*
* If there are other note formats to support, this will need updating.
*/
if ((entry_data[0x06] == 0xCA) &&
(entry_data[0x07] == 0xFE))
{
entry_structure_t oldentry;
long filesize;
// Fixup the inode number (0xCAFE is invalid)
entry_data[0x06] = 0x00;
entry_data[0x07] = 0x05; // BLOCK_VALID_FIRST;
if (0 != mempak_parse_entry(entry_data, &entry)) {
fprintf(stderr, "Error loading note (invalid)\n");
fclose(fptr);
return -1;
}
fseek(fptr, 0, SEEK_END);
filesize = ftell(fptr);
fseek(fptr, 32, SEEK_SET);
// Remove the note header
filesize -= 32;
if (filesize % MEMPAK_BLOCK_SIZE) {
fprintf(stderr, "Invalid note file size\n");
fclose(fptr);
return -1;
}
entry.blocks = filesize / MEMPAK_BLOCK_SIZE;
printf("Note size: %d blocks\n", entry.blocks);
printf("Note name: %s\n", entry.name);
if (entry.blocks > free_blocks) {
fprintf(stderr, "Not enough space (note is %d blocks and only %d free blocks in mempak)\n",
entry.blocks, free_blocks);
fclose(fptr);
return -2;
}
data = calloc(1, entry.blocks * MEMPAK_BLOCK_SIZE);
if (!data) {
perror("calloc");
fclose(fptr);
return -1;
}
if (1 != fread(data, entry.blocks * MEMPAK_BLOCK_SIZE, 1, fptr)) {
perror("could not load note data");
free(data);
fclose(fptr);
return -1;
}
if (dst_note_id == -1) { // Auto (first free note)
if (0 != mempak_findFreeNote(mpk, &oldentry, note_id)) {
fprintf(stderr, "Could not find an empty note\n");
free(data);
fclose(fptr);
return -1;
}
} else { // Specific note
get_mempak_entry(mpk, dst_note_id, &oldentry);
if (oldentry.valid) {
printf("Overwriting note %d\n", dst_note_id);
delete_mempak_entry(mpk, &oldentry);
} else {
fprintf(stderr, "No note id %d\n", dst_note_id);
free(data);
fclose(fptr);
return -1;
}
if (note_id)
*note_id = dst_note_id;
}
res = write_mempak_entry_data(mpk, &entry, data);
if (res != 0) {
fprintf(stderr, "Failed to write note (error %d)\n", res);
free(data);
fclose(fptr);
return -1;
}
return 0;
} else {
fprintf(stderr, "Input file does not appear to be in a supported format.\n");
}
if (data)
free(data);
fclose(fptr);
return -1;
}
int mempak_exportNote(mempak_structure_t *mpk, int note_id, const char *dst_filename)
{
FILE *fptr;
entry_structure_t note_header;
unsigned char databuf[0x10000];
if (!mpk)
return -1;
if (0 != get_mempak_entry(mpk, note_id, &note_header)) {
fprintf(stderr, "Error accessing note\n");
return -1;
}
if (!note_header.valid) {
fprintf(stderr, "Invaid note\n");
return -1;
}
if (0 != read_mempak_entry_data(mpk, &note_header, databuf)) {
fprintf(stderr, "Error accessing note data\n");
return -1;
}
fptr = fopen(dst_filename, "wb");
if (!fptr) {
perror("fopen");
return -1;
}
/* For compatibility with bryc's javascript mempak editor[1], I set
* the inode number to 0xCAFE.
*
* [1] https://github.com/bryc/mempak
*/
note_header.raw_data[0x06] = 0xCA;
note_header.raw_data[0x07] = 0xFE;
fwrite(note_header.raw_data, 32, 1, fptr);
fwrite(databuf, MEMPAK_BLOCK_SIZE, note_header.blocks, fptr);
fclose(fptr);
return 0;
}
int mempak_saveToFile(mempak_structure_t *mpk, const char *dst_filename, unsigned char format)
{
FILE *fptr;
int i;
if (!mpk)
return -1;
fptr = fopen(dst_filename, "wb");
if (!fptr) {
perror("fopen");
return -1;
}
switch(format)
{
default:
fclose(fptr);
return -1;
case MPK_FORMAT_MPK:
fwrite(mpk->data, sizeof(mpk->data), 1, fptr);
break;
case MPK_FORMAT_MPK4:
fwrite(mpk->data, sizeof(mpk->data), 1, fptr);
fwrite(mpk->data, sizeof(mpk->data), 1, fptr);
fwrite(mpk->data, sizeof(mpk->data), 1, fptr);
fwrite(mpk->data, sizeof(mpk->data), 1, fptr);
break;
case MPK_FORMAT_N64:
// Note: This should work well for files that will
// be imported by non-official software which typically
// only look for the 123-456-STD header and then
// seek to the data.
//
// Real .N64 files contain more info other info. Often
// 0x12: 01 00 00 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 00
// ....
// 0x3F: 00
//
// Then at 0x40, there are 0x1000 bytes. I think there are 256
// bytes available for each of block. See comments in
// mempak_loadFromFile for more info.
fprintf(fptr, "123-456-STD");
fseek(fptr, DEXDRIVE_COMMENT_OFFSET, SEEK_SET);
for (i=0; i<MEMPAK_NUM_NOTES; i++) {
unsigned char tmp = 0;
fwrite(mpk->note_comments[i], 255, 1, fptr);
// I'm not sure about the exact convention of the
// original format. Is is that comments are zero-terminated,
// but if the length is 256 then non-terminated (implcit termination?)
//
// Just to make sure nothing crashes by loading a file generated
// by this tool, I make sure there is always a zero.
fwrite(&tmp, 1, 1, fptr);
}
fseek(fptr, DEXDRIVE_DATA_OFFSET, SEEK_SET);
fwrite(mpk->data, sizeof(mpk->data), 1, fptr);
break;
}
fclose(fptr);
return 0;
}
mempak_structure_t *mempak_loadFromFile(const char *filename)
{
FILE *fptr;
long file_size;
int i;
int num_images = -1;
long offset = 0;
mempak_structure_t *mpk;
mpk = calloc(1, sizeof(mempak_structure_t));
if (!mpk) {
perror("calloc");
return NULL;
}
fptr = fopen(filename, "rb");
if (!fptr) {
perror("fopen");
free(mpk);
return NULL;
}
fseek(fptr, 0, SEEK_END);
file_size = ftell(fptr);
fseek(fptr, 0, SEEK_SET);
printf("File size: %ld bytes\n", file_size);
/* Raw binary images. Those can contain more than one card's data. For
* instance, Mupen64 seems to contain four saves. (I suppose each 32kB block is
* for the virtual mempak of one controller) */
for (i=1; i<=4; i++) {
if (file_size == 0x8000*i) {
num_images = i;
printf("MPK file Contains %d image(s)\n", num_images);
if (file_size == 0x8000) {
mpk->file_format = MPK_FORMAT_MPK;
} else {
mpk->file_format = MPK_FORMAT_MPK4;
}
}
}
if (num_images < 0) {
char header[11];
char *magic = "123-456-STD";
/* If the size is not a fixed multiple, it could be a .N64 file */
fread(header, 11, 1, fptr);
if (0 == memcmp(header, magic, sizeof(header))) {
printf(".N64 file detected\n");
/* At 0x40 there are often comments in .N64 files.
* The actual memory card data starts at 0x1040.
* This means there are exactly 0x1000 bytes for
* one large comment, or, since 0x1000 / 256 = 16,
* more likely one comment per note? That's what
* I'm assuming here. */
fseek(fptr, DEXDRIVE_COMMENT_OFFSET, SEEK_SET);
#if MAX_NOTE_COMMENT_SIZE != 257
#error
#endif
for (i=0; i<16; i++) {
fread(mpk->note_comments[i], 256, 1, fptr);
/* The comments appear to be zero terminated, but I don't
* know if the original tool allowed entering a maximum
* of 256 or 255 bytes. So to be safe, I use buffers of
* 257 bytes */
mpk->note_comments[i][256] = 0;
}
offset = DEXDRIVE_DATA_OFFSET;
mpk->file_format = MPK_FORMAT_N64;
}
}
fseek(fptr, offset, SEEK_SET);
fread(mpk->data, sizeof(mpk->data), 1, fptr);
fclose(fptr);
return mpk;
}
void mempak_free(mempak_structure_t *mpk)
{
if (mpk)
free(mpk);
}
const char *mempak_format2string(int fmt)
{
switch(fmt)
{
case MPK_FORMAT_MPK: return "MPK";
case MPK_FORMAT_MPK4: return "MPK4";
case MPK_FORMAT_N64: return "N64";
case MPK_FORMAT_INVALID: return "Invalid";
default:
return "Unknown";
}
}
int mempak_string2format(const char *str)
{
if (0 == strcasecmp(str, "mpk"))
return MPK_FORMAT_MPK;
if (0 == strcasecmp(str, "mpk4"))
return MPK_FORMAT_MPK4;
if (0 == strcasecmp(str, "n64"))
return MPK_FORMAT_N64;
return MPK_FORMAT_INVALID;
}
int mempak_getFilenameFormat(const char *filename)
{
char *s;
if ((s = strcasestr(filename, ".N64"))) {
if (s[4] == 0)
return MPK_FORMAT_N64;
}
if ((s = strcasestr(filename, ".MPK"))) {
if (s[4] == 0)
return MPK_FORMAT_MPK4;
}
return MPK_FORMAT_INVALID;
}
int mempak_hexdump(mempak_structure_t *pak)
{
int i,j;
for (i=0; i<MEMPAK_MEM_SIZE; i+=0x20) {
printf("%04x: ", i);
for (j=0; j<0x20; j++) {
printf("%02x ", pak->data[i+j]);
}
printf(" ");
for (j=0; j<0x20; j++) {
printf("%c", isprint(pak->data[i+j]) ? pak->data[i+j] : '.' );
}
printf("\n");
}
return 0;
}

View File

@ -1,38 +0,0 @@
#ifndef _mempak_h__
#define _mempak_h__
#define MEMPAK_MEM_SIZE 0x8000
#define MEMPAK_NUM_NOTES 16
#define MAX_NOTE_COMMENT_SIZE 257 // including 0 termination
#define MPK_FORMAT_INVALID 0
#define MPK_FORMAT_MPK 1
#define MPK_FORMAT_MPK4 2 // MPK + 3 times 32kB padding
#define MPK_FORMAT_N64 3
typedef struct mempak_structure
{
unsigned char data[MEMPAK_MEM_SIZE];
unsigned char file_format;
char note_comments[MEMPAK_NUM_NOTES][MAX_NOTE_COMMENT_SIZE];
} mempak_structure_t;
mempak_structure_t *mempak_new(void);
mempak_structure_t *mempak_loadFromFile(const char *filename);
int mempak_saveToFile(mempak_structure_t *mpk, const char *dst_filename, unsigned char format);
int mempak_exportNote(mempak_structure_t *mpk, int note_id, const char *dst_filename);
int mempak_importNote(mempak_structure_t *mpk, const char *notefile, int dst_note_id, int *note_id);
void mempak_free(mempak_structure_t *mpk);
int mempak_getFilenameFormat(const char *filename);
int mempak_string2format(const char *str);
const char *mempak_format2string(int fmt);
int mempak_hexdump(mempak_structure_t *pak);
#include "mempak_fs.h"
#endif // _mempak_h__

View File

@ -1,100 +0,0 @@
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <getopt.h>
#include "mempak.h"
#define DEFAULT_FORMAT_STR "n64"
static void print_usage(void)
{
printf("Usage: ./mempak_convert in_file out_file <options>\n");
printf("\n");
printf("Options:\n");
printf(" -h Display help\n");
printf(" -f format Write file in specified format (default: %s)\n", DEFAULT_FORMAT_STR);
printf("\n");
printf("Formats:\n");
printf(" mpk Standard 32kB .mpk file format\n");
printf(" mpk4 128kB .mpk file (4 copies or the 32kB block)\n");
printf(" n64 .N64 file format\n");
}
int main(int argc, char **argv)
{
mempak_structure_t *mpk;
const char *infile;
const char *outfile;
unsigned char type;
struct option long_options[] = {
{ "format", required_argument, 0, 'f' },
{ "help", no_argument, 0, 'h' },
{ }, // terminator
};
const char *format = DEFAULT_FORMAT_STR;
if (argc < 2) {
print_usage();
return 1;
}
while(1) {
int c;
c = getopt_long(argc, argv, "f:h", long_options, NULL);
if (c==-1)
break;
switch(c)
{
case 'h':
print_usage();
return 0;
case 'f':
format = optarg;
break;
case '?':
fprintf(stderr, "Unknown argument. Try -h\n");
return -1;
}
}
type = mempak_string2format(format);
if (type == MPK_FORMAT_INVALID) {
fprintf(stderr, "Unknown format specified\n");
return -1;
}
infile = argv[optind];
outfile = argv[optind+1];
mpk = mempak_loadFromFile(infile);
if (!mpk) {
fprintf(stderr, "Could not load mempak file '%s'\n", infile);
return 1;
}
printf("Loaded file '%s' (%s format)\n", infile, mempak_format2string(mpk->file_format));
mempak_saveToFile(mpk, outfile, type);
mempak_free(mpk);
printf("Wrote file '%s' in %s format\n", outfile, mempak_format2string(type));
return 0;
}

View File

@ -1,56 +0,0 @@
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include "mempak.h"
int main(int argc, char **argv)
{
const char *infile;
const char *outfile;
int note_id;
mempak_structure_t *mpk;
if (argc < 4) {
printf("Usage: ./mempak_extract_note in_file note_id out_file\n");
printf("\n");
printf("Where:\n");
printf(" in_file The input file (full mempak image .mpk/.n64)\n");
printf(" note_id The id of the note (as shown by mempak_ls)\n");
printf(" out_file The output filename (eg: abc.note)\n");
return 1;
}
infile = argv[1];
note_id = atoi(argv[2]);
outfile = argv[3];
mpk = mempak_loadFromFile(infile);
if (!mpk) {
return 1;
}
if (mempak_exportNote(mpk, note_id, outfile)) {
fprintf(stderr, "could not export note\n");
}
printf("Exported note %d to file '%s'\n", note_id, outfile);
mempak_free(mpk);
return 0;
}

View File

@ -1,93 +0,0 @@
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <getopt.h>
#include "mempak.h"
#define DEFAULT_FORMAT_STR "n64"
static void print_usage(void)
{
printf("Usage: ./mempak_format file <options>\n");
printf("\n");
printf("Options:\n");
printf(" -h Display help\n");
printf(" -f format Write file in specified format (default: %s)\n", DEFAULT_FORMAT_STR);
printf("\n");
printf("Formats:\n");
printf(" mpk Standard 32kB .mpk file format\n");
printf(" mpk4 128kB .mpk file (4 copies or the 32kB block)\n");
printf(" n64 .N64 file format\n");
}
int main(int argc, char **argv)
{
mempak_structure_t *mpk;
const char *outfile;
unsigned char type;
struct option long_options[] = {
{ "format", required_argument, 0, 'f' },
{ "help", no_argument, 0, 'h' },
{ }, // terminator
};
const char *format = DEFAULT_FORMAT_STR;
if (argc < 2) {
print_usage();
return 1;
}
while(1) {
int c;
c = getopt_long(argc, argv, "f:h", long_options, NULL);
if (c==-1)
break;
switch(c)
{
case 'h':
print_usage();
return 0;
case 'f':
format = optarg;
break;
case '?':
fprintf(stderr, "Unknown argument. Try -h\n");
return -1;
}
}
type = mempak_string2format(format);
if (type == MPK_FORMAT_INVALID) {
fprintf(stderr, "Unknown format specified\n");
return -1;
}
outfile = argv[optind];
mpk = mempak_new();
if (!mpk) {
return 1;
}
mempak_saveToFile(mpk, outfile, type);
mempak_free(mpk);
printf("Wrote empty (formatted) memory card file '%s' in '%s' format\n", outfile, mempak_format2string(type));
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,82 +0,0 @@
/**
* @file mempak.h
* @brief Mempak Filesystem Routines
* @ingroup mempak
*/
#ifndef __LIBDRAGON_MEMPAK_H
#define __LIBDRAGON_MEMPAK_H
#include "mempak.h"
#include <stdint.h>
/**
* @addtogroup mempak
* @{
*/
/** @brief Size in bytes of a Mempak block */
#define MEMPAK_BLOCK_SIZE 256
/**
* @brief Structure representing a save entry in a mempak
*/
typedef struct entry_structure
{
/** @brief Vendor ID */
uint32_t vendor;
/** @brief Game ID */
uint16_t game_id;
/** @brief Inode pointer */
uint16_t inode;
/** @brief Intended region */
uint8_t region;
/** @brief Number of blocks used by this entry.
* @see MEMPAK_BLOCK_SIZE */
uint8_t blocks;
/** @brief Validity of this entry. */
uint8_t valid;
/** @brief ID of this entry */
uint8_t entry_id;
/**
* @brief Name of this entry
*
* The complete list of valid ASCII characters in a note name is:
*
* <pre>
* ABCDEFGHIJKLMNOPQRSTUVWXYZ!"#`*+,-./:=?\@
* </pre>
*
* The space character is also allowed. Any other character will be
* converted to a space before writing to the mempak.
*
* @see #__n64_to_ascii and #__ascii_to_n64
*/
char name[19];
/** @brief A copy of the raw note data (from the note table). */
unsigned char raw_data[32];
} entry_structure_t;
#ifdef __cplusplus
extern "C" {
#endif
int read_mempak_sector( mempak_structure_t *pak, int sector, uint8_t *sector_data );
int write_mempak_sector( mempak_structure_t *pak, int sector, uint8_t *sector_data );
int validate_mempak( mempak_structure_t *pak );
int get_mempak_free_space( mempak_structure_t *pak );
int get_mempak_entry( mempak_structure_t *pak, int entry, entry_structure_t *entry_data );
int format_mempak( mempak_structure_t *pak );
int read_mempak_entry_data( mempak_structure_t *pak, entry_structure_t *entry, uint8_t *data );
int write_mempak_entry_data( mempak_structure_t *pak, entry_structure_t *entry, uint8_t *data );
int delete_mempak_entry( mempak_structure_t *pak, entry_structure_t *entry );
int mempak_parse_entry( const uint8_t *tnote, entry_structure_t *note );
#ifdef __cplusplus
}
#endif
/** @} */ /* mempak */
#endif

View File

@ -1,322 +0,0 @@
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "gcn64lib.h"
#include "gcn64.h"
#include "mempak.h"
#include "mempak_gcn64usb.h"
#include "hexdump.h"
#include "../gcn64_protocol.h"
#include "../requests.h"
/* __calc_address_crc is from libdragon which is public domain. */
/**
* @brief Calculate the 5 bit CRC on a mempak address
*
* This function, given an address intended for a mempak read or write, will
* calculate the CRC on the address, returning the corrected address | CRC.
*
* @param[in] address
* The mempak address to calculate CRC over
*
* @return The mempak address | CRC
*/
static uint16_t __calc_address_crc( uint16_t address )
{
/* CRC table */
uint16_t xor_table[16] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x15, 0x1F, 0x0B, 0x16, 0x19, 0x07, 0x0E, 0x1C, 0x0D, 0x1A, 0x01 };
uint16_t crc = 0;
int i;
/* Make sure we have a valid address */
address &= ~0x1F;
/* Go through each bit in the address, and if set, xor the right value into the output */
for( i = 15; i >= 5; i-- )
{
/* Is this bit set? */
if( ((address >> i) & 0x1) )
{
crc ^= xor_table[i];
}
}
/* Just in case */
crc &= 0x1F;
/* Create a new address with the CRC appended */
return address | crc;
}
/* __calc_data_crc is from libdragon which is public domain. */
/**
* @brief Calculate the 8 bit CRC over a 32-byte block of data
*
* This function calculates the 8 bit CRC appropriate for checking a 32-byte
* block of data intended for or retrieved from a mempak.
*
* @param[in] data
* Pointer to 32 bytes of data to run the CRC over
*
* @return The calculated 8 bit CRC over the data
*/
static uint8_t __calc_data_crc( uint8_t *data )
{
uint8_t ret = 0;
int i,j;
for( i = 0; i <= 32; i++ )
{
for( j = 7; j >= 0; j-- )
{
int tmp = 0;
if( ret & 0x80 )
{
tmp = 0x85;
}
ret <<= 1;
if( i < 32 )
{
if( data[i] & (0x01 << j) )
{
ret |= 0x1;
}
}
ret ^= tmp;
}
}
return ret;
}
int gcn64lib_mempak_readBlock(gcn64_hdl_t hdl, unsigned short addr, unsigned char dst[32])
{
unsigned char cmd[64];
//int cmdlen;
int n;
uint16_t addr_crc;
unsigned char crc;
addr_crc = __calc_address_crc(addr);
cmd[0] = N64_EXPANSION_READ;
cmd[1] = addr_crc>>8; // Address high byte
cmd[2] = addr_crc&0xff; // Address low byte
n = gcn64lib_rawSiCommand(hdl, 0, cmd, 3, cmd, sizeof(cmd));
if (n != 33) {
printf("Hey! %d\n", n);
return -1;
}
memcpy(dst, cmd, 0x20);
crc = __calc_data_crc(dst);
if (crc != cmd[32]) {
fprintf(stderr, "Bad CRC reading address 0x%04x\n", addr);
return -1;
}
return 0x20;
}
int gcn64lib_mempak_detect(gcn64_hdl_t hdl)
{
unsigned char buf[40];
int res;
unsigned short addr = __calc_address_crc(0x8000);
int first_read, second_read;
buf[0] = N64_GET_CAPABILITIES;
res = gcn64lib_rawSiCommand(hdl, 0, buf, 1, buf, sizeof(buf));
if (res < 0) {
return -1;
}
if (res != 3) {
return -1;
}
if (!(buf[2] & 0x01)) {
printf("No accessory connected\n");
return -1;
}
/* first write 32 0xFEs */
memset(buf, 0xfe, 32);
res = gcn64lib_n64_expansionWrite(hdl, addr, buf, 32);
if (res < 0) {
return -1;
}
if (res != 0xE1) {
return -1;
}
/* Read back (normally zeros) */
res = gcn64lib_n64_expansionRead(hdl, addr, buf, sizeof(buf));
if (res < 0) {
return -1;
}
first_read = buf[0];
/* Now write 32 0x80s */
memset(buf, 0x80, 32);
res = gcn64lib_n64_expansionWrite(hdl, addr, buf, 32);
if (res < 0) {
return -1;
}
/* Normally, read back (0x00 on memory card, 0x80 on rumble pak)
*
* But this simple detection method (from libdragon) does not detect one of my
* memory cards (it looks like a rumble pack).
*
* But I found out that the values that are read back are just always equal to while
* for other hardware, reading after writing the 0xfe values always seem to return 0x00.
*/
res = gcn64lib_n64_expansionRead(hdl, addr, buf, sizeof(buf));
if (res < 0) {
printf("failed to detect mempak: %d\n", res);
return -1;
}
second_read = buf[0];
// Values seen here are
//
// - Official Nintendo rumble pack: 0x00
// - Yobo rumble pack: 0x00
// - Yobo mempak: 0x00
// - Unknown "super memory card 1000": 0xFE
//
if (first_read == 0xfe) {
printf("super memory card 1000 probably detected\n");
return 0;
}
// Values seen here are
//
// - Official Nintendo rumble pack: 0x80
// - Yobo rumble pack: 0x80
// - Yobo mempak: 0x00
// - Unknown "super memory card 1000": 0x80 (but this card is catched above)
if (second_read == 0x80) {
return -1; // rumble
} else {
return 0;
}
}
int gcn64lib_mempak_writeBlock(gcn64_hdl_t hdl, unsigned short addr, unsigned char data[32])
{
return gcn64lib_n64_expansionWrite(hdl, __calc_address_crc(addr), data, 32);
}
/**
* \brief Read a physical mempak
* \param hdl The Adapter handler
* \param channel The adapter channel (for multi-port adapters)
* \param pak Pointer to mempak_structure pointer to store the new mempak
* \param progressCb Callback to notify read progress (called after each block). The callback can return non-zero to abort.
* \return 0: Success, -1: No mempak, -2: IO/error, -3: Other errors, -4: Aborted
*/
int gcn64lib_mempak_download(gcn64_hdl_t hdl, int channel, mempak_structure_t **mempak, int (*progressCb)(int cur_addr, void *ctx), void *ctx)
{
mempak_structure_t *pak;
unsigned short addr;
if (!mempak) {
return -3;
}
if (gcn64lib_mempak_detect(hdl)) {
return -1;
}
pak = calloc(1, sizeof(mempak_structure_t));
if (!pak) {
return -3;
}
pak->file_format = MPK_FORMAT_MPK;
for (addr = 0x0000; addr < MEMPAK_MEM_SIZE; addr+= 0x20)
{
if (gcn64lib_mempak_readBlock(hdl, addr, &pak->data[addr]) != 0x20) {
fprintf(stderr, "Error: Short read\n");
free(pak);
return -2;
}
if (progressCb) {
if (progressCb(addr, ctx)) {
return -4;
}
}
}
*mempak = pak;
return 0;
}
int gcn64lib_mempak_upload(gcn64_hdl_t hdl, int channel, mempak_structure_t *pak, int (*progressCb)(int cur_addr, void *ctx), void *ctx)
{
unsigned short addr;
unsigned char readback[0x20];
int res;
if (!pak) {
return -3;
}
if (gcn64lib_mempak_detect(hdl)) {
return -1;
}
for (addr = 0x0000; addr < MEMPAK_MEM_SIZE; addr+= 0x20)
{
res = gcn64lib_mempak_writeBlock(hdl, addr, &pak->data[addr]);
if (res < 0) {
fprintf(stderr, "Write error\n");
return -2;
}
if (0x20 != gcn64lib_mempak_readBlock(hdl, addr, readback)) {
// TODO : Why not retry?
fprintf(stderr, "readback failed\n");
return -2;
}
if (memcmp(readback, &pak->data[addr], 0x20)) {
fprintf(stderr, "Readback compare failed\n");
return -2;
}
if (progressCb) {
if (progressCb(addr, ctx)) {
return -4;
}
}
}
return 0;
}

View File

@ -1,13 +0,0 @@
#ifndef _mempak_gcn64usb_h__
#define _mempak_gcn64usb_h__
#include "mempak.h"
int gcn64lib_mempak_detect(gcn64_hdl_t hdl);
int gcn64lib_mempak_readBlock(gcn64_hdl_t hdl, unsigned short addr, unsigned char dst[32]);
int gcn64lib_mempak_writeBlock(gcn64_hdl_t hdl, unsigned short addr, unsigned char data[32]);
int gcn64lib_mempak_download(gcn64_hdl_t hdl, int channel, mempak_structure_t **mempak, int (*progressCb)(int cur_addr, void *ctx), void *ctx);
int gcn64lib_mempak_upload(gcn64_hdl_t hdl, int channel, mempak_structure_t *pak, int (*progressCb)(int cur_addr, void *ctx), void *ctx);
#endif // _mempak_gcn64usb_h__

View File

@ -1,117 +0,0 @@
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include "mempak.h"
static void print_usage(void)
{
printf("Usage: ./mempak_insert_note pakfile notefile\n");
printf("\n");
printf("Options:\n");
printf(" -h, --help Display help\n");
printf(" -c, --comment Comment Note comment (only works for .N64 files)\n");
printf(" -d, --dst id Overwrite a specific note slot (0-15)\n");
}
int main(int argc, char **argv)
{
const char *pakfile;
const char *notefile;
mempak_structure_t *mpk;
struct option long_options[] = {
{ "help", no_argument, 0, 'h' },
{ "comment", required_argument, 0, 'c' },
{ "dst", required_argument, 0, 'd' },
{ }, // terminator
};
const char *comment = NULL;
int used_note_id = -1;
int dst_id = -1;
if (argc < 3) {
print_usage();
return 1;
}
while(1) {
int c;
c = getopt_long(argc, argv, "f:hc:d:", long_options, NULL);
if (c==-1)
break;
switch(c)
{
case 'h':
print_usage();
return 0;
case 'c':
comment = optarg;
if (strlen(optarg) > (MAX_NOTE_COMMENT_SIZE-2)) {
fprintf(stderr, "Comment too long (%d characters max.)\n", (MAX_NOTE_COMMENT_SIZE-2));
return -1;
}
break;
case 'd':
dst_id = atoi(optarg);
break;
case '?':
fprintf(stderr, "Unknown argument. Try -h\n");
return -1;
}
}
pakfile = argv[optind];
notefile = argv[optind+1];
mpk = mempak_loadFromFile(pakfile);
if (!mpk) {
return 1;
}
printf("Loaded pakfile in %s format.\n", mempak_format2string(mpk->file_format));
if (mempak_importNote(mpk, notefile, dst_id, &used_note_id)) {
fprintf(stderr, "could not export note\n");
mempak_free(mpk);
return 0;
}
printf("Note imported and written to slot %d\n", used_note_id);
if (comment) {
if (mpk->file_format != MPK_FORMAT_N64) {
printf("Warning: Ignoring comment since it cannot be stored in %s file format. Use the N64 format instead.\n", mempak_format2string(mpk->file_format));
} else {
strncpy(mpk->note_comments[used_note_id], comment, MAX_NOTE_COMMENT_SIZE);
mpk->note_comments[used_note_id][256] = 0;
mpk->note_comments[used_note_id][255] = 0;
}
}
if (0 != mempak_saveToFile(mpk, pakfile, mpk->file_format)) {
fprintf(stderr, "could not write to memory pak file\n");
}
mempak_free(mpk);
return 0;
}

View File

@ -1,78 +0,0 @@
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include "mempak.h"
int main(int argc, char **argv)
{
const char *infile;
mempak_structure_t *mpk;
int note;
int res;
if (argc < 2) {
printf("Usage: ./mempak_ls file\n");
printf("\n");
printf("Raw files (.MPK, .BIN) and Dexdrive (.N64) formats accepted.\n");
return 1;
}
infile = argv[1];
mpk = mempak_loadFromFile(infile);
if (!mpk) {
return 1;
}
printf("Mempak image loaded. Image type %d (%s)\n", mpk->file_format, mempak_format2string(mpk->file_format));
if (0 != validate_mempak(mpk)) {
printf("Mempak invalid (not formatted or corrupted)\n");
goto done;
}
printf("Mempak content is valid\n");
printf("Block usage: %d / %d\n", 123-get_mempak_free_space(mpk), 123);
for (note = 0; note<MEMPAK_NUM_NOTES; note++) {
entry_structure_t note_data;
printf("Note %d: ", note);
res = get_mempak_entry(mpk, note, &note_data);
if (res) {
printf("Error!\n");
} else {
if (note_data.valid) {
printf("%s (%d blocks) ", note_data.name, note_data.blocks);
// printf("%08x ", note_data.vendor);
// printf("%04x ", note_data.game_id);
// printf("%02x ", note_data.region);
if (strlen(mpk->note_comments[note]) > 0) {
printf("{ %s }", mpk->note_comments[note]);
}
printf("\n");
} else {
printf("Free\n");
}
}
}
done:
mempak_free(mpk);
return 0;
}

View File

@ -1,106 +0,0 @@
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include "mempak.h"
static void print_usage(void)
{
printf("Usage: ./mempak_rm pakfile note_id\n");
printf("\n");
printf("Options:\n");
printf(" -h, --help Display help\n");
}
int main(int argc, char **argv)
{
const char *pakfile;
mempak_structure_t *mpk;
struct option long_options[] = {
{ "help", no_argument, 0, 'h' },
{ }, // terminator
};
int noteid;
entry_structure_t entry;
if (argc < 3) {
print_usage();
return 1;
}
while(1) {
int c;
c = getopt_long(argc, argv, "h", long_options, NULL);
if (c==-1)
break;
switch(c)
{
case 'h':
print_usage();
return 0;
case '?':
fprintf(stderr, "Unknown argument. Try -h\n");
return -1;
}
}
pakfile = argv[optind];
noteid = atoi(argv[optind+1]);
if (noteid < 0 || noteid > 15) {
fprintf(stderr, "Note id out of range (0-15)\n");
return -1;
}
mpk = mempak_loadFromFile(pakfile);
if (!mpk) {
return -1;
}
printf("Loaded pakfile in %s format.\n", mempak_format2string(mpk->file_format));
if (0 != get_mempak_entry(mpk, noteid, &entry)) {
fprintf(stderr, "Could not get note id %d\n", noteid);
mempak_free(mpk);
return -1;
}
if (!entry.valid) {
fprintf(stderr, "Note %d is already free\n", noteid);
mempak_free(mpk);
return -1;
}
printf("Deleting note %d (%d blocks)\n", noteid, entry.blocks);
if (0 != delete_mempak_entry(mpk, &entry)) {
fprintf(stderr, "Error deleting entry\n");
mempak_free(mpk);
return -1;
}
if (0 != mempak_saveToFile(mpk, pakfile, mpk->file_format)) {
fprintf(stderr, "could not write to memory pak file\n");
}
mempak_free(mpk);
return 0;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@ -1,22 +0,0 @@
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <windows.h>
void sleep(int s)
{
Sleep(s*1000);
}

View File

@ -1,6 +0,0 @@
#ifndef _sleep_h__
#define _sleep_h__
void sleep(int s);
#endif // _sleep_h__

View File

@ -1,49 +0,0 @@
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#ifdef TEST_IMPLEMENTATION
#include <stdio.h>
#define strcasestr my_strcasestr
char *strcasestr(const char *haystack, const char *needle);
int main(void)
{
const char *a = "sdfsdj kdkf23 34fjsdf lsdf ";
if (my_strcasestr(a, "11")) {
printf("Test 1 failed\n");
}
if (!my_strcasestr(a, "23")) {
printf("Test 2 failed\n");
}
}
#endif
char *strcasestr(const char *haystack, const char *needle)
{
while (*haystack) {
if (0==strncasecmp(haystack, needle, strlen(needle))) {
return (char*)haystack;
}
haystack++;
}
return NULL;
}

View File

@ -1,6 +0,0 @@
#ifndef _strcasestr_h__
#define _strcasestr_h_-
char *strcasestr(const char *haystack, const char *needle);
#endif // _strcasestr_h__

View File

@ -1,6 +0,0 @@
#ifndef _version_h__
#define _version_h__
#define VERSION_STR "1.1.1"
#endif