/* * Copyright (C) 2008 dhewg, #wiidev efnet * * this file is part of geckoloader * http://wiibrew.org/index.php?title=Geckoloader * * 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 2 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #ifndef _WIN32 #include #include #include #include #else #include #endif #include #include "gecko.h" #define WIILOAD_VERSION_MAYOR 0 #define WIILOAD_VERSION_MINOR 5 #define LD_TCP_PORT 4299 #define MAX_ARGS_LEN 1024 #ifndef O_BINARY #define O_BINARY 0 #endif typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; typedef unsigned long long u64; typedef signed char s8; typedef signed short s16; typedef signed int s32; typedef signed long long s64; typedef enum { false, true } bool; #ifndef __WIN32__ static const char *desc_export = "export"; #ifndef __APPLE__ static const char *desc_gecko = "/dev/ttyUSB0"; #else static const char *desc_gecko = "/dev/tty.usbserial-GECKUSB0"; #endif #else static const char *desc_export = "set"; static const char *desc_gecko = "COM4"; #endif static const char *envvar = "WIILOAD"; static bool send_gecko (const char *dev, const u8 *buf, u32 len, u32 len_un, const char *args, u16 args_len) { u8 b[4]; u32 left, block; const u8 *p; if (gecko_open (dev)) { fprintf (stderr, "unable to open the device '%s'\n", dev); return false; } printf ("sending upload request\n"); b[0] = 'H'; b[1] = 'A'; b[2] = 'X'; b[3] = 'X'; if (gecko_write (b, 4)) { gecko_close (); fprintf (stderr, "error sending data\n"); return false; } b[0] = WIILOAD_VERSION_MAYOR; b[1] = WIILOAD_VERSION_MINOR; b[2] = (args_len >> 8) & 0xff; b[3] = args_len & 0xff; if (gecko_write (b, 4)) { gecko_close (); fprintf (stderr, "error sending data\n"); return false; } printf ("sending file size (%u bytes)\n", len); b[0] = (len >> 24) & 0xff; b[1] = (len >> 16) & 0xff; b[2] = (len >> 8) & 0xff; b[3] = len & 0xff; if (gecko_write (b, 4)) { gecko_close (); fprintf (stderr, "error sending data\n"); return false; } b[0] = (len_un >> 24) & 0xff; b[1] = (len_un >> 16) & 0xff; b[2] = (len_un >> 8) & 0xff; b[3] = len_un & 0xff; if (gecko_write (b, 4)) { gecko_close (); fprintf (stderr, "error sending data\n"); return false; } printf ("sending data"); fflush (stdout); left = len; p = buf; while (left) { block = left; if (block > 63488) block = 63488; left -= block; if (gecko_write (p, block)) { fprintf (stderr, "error sending block\n"); break; } p += block; printf ("."); fflush (stdout); } printf ("\n"); if (args_len) { printf ("sending arguments (%u bytes)\n", args_len); if (gecko_write ((u8 *) args, args_len)) { gecko_close (); return false; } } gecko_close (); return true; } static bool tcp_write (int s, const u8 *buf, u32 len) { s32 left, block; const u8 *p; left = len; p = buf; while (left) { block = send (s, p, left, 0); if (block < 0) { perror ("send failed"); return false; } left -= block; p += block; } return true; } static bool send_tcp (const char *host, const u8 *buf, u32 len, u32 len_un, const char *args, u16 args_len) { struct sockaddr_in sa; struct hostent *he; int s, bc; u8 b[4]; off_t left, block; const u8 *p; #ifdef __WIN32__ WSADATA wsa_data; if (WSAStartup (MAKEWORD(2,2), &wsa_data)) { printf ("WSAStartup failed\n"); return false; } #endif memset (&sa, 0, sizeof (sa)); sa.sin_addr.s_addr = inet_addr (host); if (sa.sin_addr.s_addr == INADDR_NONE) { printf ("resolving %s\n", host); he = gethostbyname (host); if (!he) { #ifndef __WIN32__ herror ("error resolving hostname"); #else fprintf (stderr, "error resolving hostname\n"); #endif return false; } if (he->h_addrtype != AF_INET) { fprintf (stderr, "unsupported address"); return false; } sa.sin_addr.s_addr = *((u32 *) he->h_addr); } s = socket (PF_INET, SOCK_STREAM, 0); if (s < 0) { perror ("error creating socket"); return false; } sa.sin_port = htons (LD_TCP_PORT); sa.sin_family = AF_INET; printf ("connecting to %s:%d\n", inet_ntoa (sa.sin_addr), LD_TCP_PORT); if (connect (s, (struct sockaddr *) &sa, sizeof (sa)) == -1) { perror ("error connecting"); close (s); return false; } printf ("sending upload request\n"); b[0] = 'H'; b[1] = 'A'; b[2] = 'X'; b[3] = 'X'; if (!tcp_write (s, b, 4)) { close (s); return false; } b[0] = WIILOAD_VERSION_MAYOR; b[1] = WIILOAD_VERSION_MINOR; b[2] = (args_len >> 8) & 0xff; b[3] = args_len & 0xff; if (!tcp_write (s, b, 4)) { close (s); return false; } printf ("sending file size (%u bytes)\n", len); b[0] = (len >> 24) & 0xff; b[1] = (len >> 16) & 0xff; b[2] = (len >> 8) & 0xff; b[3] = len & 0xff; if (!tcp_write (s, b, 4)) { close (s); return false; } b[0] = (len_un >> 24) & 0xff; b[1] = (len_un >> 16) & 0xff; b[2] = (len_un >> 8) & 0xff; b[3] = len_un & 0xff; if (!tcp_write (s, b, 4)) { close (s); return false; } printf ("sending data"); fflush (stdout); left = len; p = buf; bc = 0; while (left) { block = left; if (block > 4 * 1024) block = 4 * 1024; left -= block; if (!tcp_write (s, p, block)) { close (s); return false; } p += block; bc++; if (!(bc % 16)) { printf ("."); fflush (stdout); } } printf ("\n"); if (args_len) { printf ("sending arguments (%u bytes)\n", args_len); if (!tcp_write (s, (u8 *) args, args_len)) { close (s); return false; } } #ifndef __WIN32__ close (s); #else shutdown (s, SD_SEND); closesocket (s); WSACleanup (); #endif return true; } static void usage (const char *argv0) { fprintf (stderr, "set the environment variable %s to a valid " "destination.\n\n" "examples:\n" "\tusbgecko mode:\n" "\t\t%s %s=%s\n\n" "\ttcp mode:\n" "\t\t%s %s=tcp:wii\n" "\t\t%s %s=tcp:192.168.0.30\n\n" "usage:\n" "\t%s \n\n", envvar, desc_export, envvar, desc_gecko, desc_export, envvar, desc_export, envvar, argv0); exit (EXIT_FAILURE); } int main (int argc, char **argv) { int fd; struct stat st; char *ev; bool compress = true; u8 *buf, *bufz; off_t fsize; uLongf bufzlen = 0; u32 len, len_un; int i, c; char args[MAX_ARGS_LEN]; char *arg_pos; u16 args_len, args_left; bool res; printf ("wiiload v%u.%u\n" "coded by dhewg, #wiidev efnet\n\n", WIILOAD_VERSION_MAYOR, WIILOAD_VERSION_MINOR); if (argc < 2) usage (*argv); ev = getenv (envvar); if (!ev) usage (*argv); fd = open (argv[1], O_RDONLY | O_BINARY); if (fd < 0) { perror ("error opening the file"); exit (EXIT_FAILURE); } if (fstat (fd, &st)) { close (fd); perror ("error stat'ing the file"); exit (EXIT_FAILURE); } fsize = st.st_size; if (fsize < 64 || fsize > 20 * 1024 * 1024) { close (fd); fprintf (stderr, "error: invalid file size\n"); exit (EXIT_FAILURE); } buf = malloc (fsize); if (!buf) { close (fd); fprintf (stderr, "out of memory\n"); exit (EXIT_FAILURE); } if (read (fd, buf, fsize) != fsize) { close (fd); free (buf); perror ("error reading the file"); exit (EXIT_FAILURE); } close (fd); len = fsize; len_un = 0; if (!memcmp(buf, "PK\x03\x04", 4)) compress = false; if (compress) { bufzlen = (uLongf) ((float) fsize * 1.02); bufz = malloc (bufzlen); if (!bufz) { fprintf (stderr, "out of memory\n"); exit (EXIT_FAILURE); } printf("compressing %u bytes...", (u32) fsize); fflush(stdout); res = compress2 (bufz, &bufzlen, buf, fsize, 6); if (res != Z_OK) { free(buf); free(bufz); fprintf (stderr, "error compressing data: %d\n", res); exit (EXIT_FAILURE); } if (bufzlen < (u32) fsize) { printf(" %.2f%%\n", 100.0f * (float) bufzlen / (float) fsize); len = bufzlen; len_un = fsize; free(buf); buf = bufz; } else { printf(" compressed size gained size, discarding\n"); free(bufz); } } args_len = 0; arg_pos = args; args_left = MAX_ARGS_LEN; c = snprintf (arg_pos, args_left, "%s", basename (argv[1])); arg_pos += c + 1; args_left -= c + 1; if (argc > 2) { for (i = 2; i < argc; ++i) { c = snprintf (arg_pos, args_left, "%s", argv[i]); if (c >= args_left) { free (buf); fprintf (stderr, "argument string too long\n"); exit (EXIT_FAILURE); } arg_pos += c + 1; args_left -= c + 1; } if (args_left < 1) { free (buf); fprintf (stderr, "argument string too long\n"); exit (EXIT_FAILURE); } } arg_pos[0] = 0; args_len = MAX_ARGS_LEN - args_left + 1; if (strncmp (ev, "tcp:", 4)) { if (stat (ev, &st)) usage (*argv); res = send_gecko (ev, buf, len, len_un, args, args_len); } else { if (strlen (ev) < 5) usage (*argv); res = send_tcp (&ev[4], buf, len, len_un, args, args_len); } if (res) printf ("done.\n"); else printf ("transfer failed.\n"); free (buf); return 0; }