Files
vbagx/source/ngc/http.cpp
dborth 2d9b7ef1f9 [1.0.8 - April 4, 2009]
* "Match Wii Game" controls option! Games that have a Wii equivalent can be
  played using the controls for that Wii game. For example all Zelda games
  can be played with Twilight Princess controls. See the Instructions section
  below for important details.
* Rotation/Tilt sensor games all work
* Solar sensors (Boktai 1/2/3)
* Rumble (except for games that rely on Gameboy Player)
* Keyboard
* PAL support, finally!
* New scaling options, choose how much stretching you want
* Colourised games now partially work but still have distortion
* "Corvette" no longer has a screwed up palette (but still crashes)
* Triggers net reconnection on SMB failure
* Source code refactored, and project file added
* Instructions section added to this readme file
2009-04-10 03:16:28 +00:00

432 lines
6.5 KiB
C++

/****************************************************************************
* Visual Boy Advance GX
*
* Tantric December 2008
*
* http.cpp
*
* HTTP operations
* Written by dhewg/bushing, modified by Tantric
***************************************************************************/
#include <malloc.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <ogcsys.h>
#include <network.h>
#include <ogc/lwp_watchdog.h>
#include <sys/types.h>
#include <sys/errno.h>
#include <fcntl.h>
#include "menudraw.h"
#include "http.h"
static s32 tcp_socket(void)
{
s32 s, res;
s = net_socket(PF_INET, SOCK_STREAM, 0);
if (s < 0)
{
return s;
}
res = net_fcntl(s, F_GETFL, 0);
if (res < 0)
{
net_close(s);
return res;
}
res = net_fcntl(s, F_SETFL, res | 4);
if (res < 0)
{
net_close(s);
return res;
}
return s;
}
static s32 tcp_connect(char *host, const u16 port)
{
struct hostent *hp;
struct sockaddr_in sa;
s32 s, res;
s64 t;
hp = net_gethostbyname(host);
if (!hp || !(hp->h_addrtype == PF_INET))
{
return errno;
}
s = tcp_socket();
if (s < 0)
return s;
memset(&sa, 0, sizeof(struct sockaddr_in));
sa.sin_family = PF_INET;
sa.sin_len = sizeof(struct sockaddr_in);
sa.sin_port = htons(port);
memcpy((char *) &sa.sin_addr, hp->h_addr_list[0], hp->h_length);
t = gettime();
while (true)
{
if (ticks_to_millisecs(diff_ticks(t, gettime())) > TCP_CONNECT_TIMEOUT)
{
net_close(s);
return -ETIMEDOUT;
}
res = net_connect(s, (struct sockaddr *) &sa,
sizeof(struct sockaddr_in));
if (res < 0)
{
if (res == -EISCONN)
break;
if (res == -EINPROGRESS || res == -EALREADY)
{
usleep(20 * 1000);
continue;
}
net_close(s);
return res;
}
break;
}
return s;
}
static char * tcp_readln(const s32 s, const u16 max_length, const u64 start_time,
const u16 timeout)
{
char *buf;
u16 c;
s32 res;
char *ret;
buf = (char *) malloc(max_length);
c = 0;
ret = NULL;
while (true)
{
if (ticks_to_millisecs(diff_ticks(start_time, gettime())) > timeout)
break;
res = net_read(s, &buf[c], 1);
if ((res == 0) || (res == -EAGAIN))
{
usleep(20 * 1000);
continue;
}
if (res < 0)
{
break;
}
if ((c > 0) && (buf[c - 1] == '\r') && (buf[c] == '\n'))
{
if (c == 1)
{
ret = strdup("");
break;
}
ret = strndup(buf, c - 1);
break;
}
c++;
if (c == max_length)
break;
}
free(buf);
return ret;
}
static bool tcp_read(const s32 s, u8 **buffer, const u32 length)
{
u8 *p;
u32 step, left, block, received;
s64 t;
s32 res;
step = 0;
p = *buffer;
left = length;
received = 0;
t = gettime();
while (left)
{
if (ticks_to_millisecs(diff_ticks(t, gettime()))
> TCP_BLOCK_RECV_TIMEOUT)
{
break;
}
block = left;
if (block > 2048)
block = 2048;
res = net_read(s, p, block);
if ((res == 0) || (res == -EAGAIN))
{
usleep(20 * 1000);
continue;
}
if (res < 0)
{
break;
}
received += res;
left -= res;
p += res;
if ((received / TCP_BLOCK_SIZE) > step)
{
t = gettime();
step++;
}
}
return left == 0;
}
static bool tcp_write(const s32 s, const u8 *buffer, const u32 length)
{
const u8 *p;
u32 step, left, block, sent;
s64 t;
s32 res;
step = 0;
p = buffer;
left = length;
sent = 0;
t = gettime();
while (left)
{
if (ticks_to_millisecs(diff_ticks(t, gettime()))
> TCP_BLOCK_SEND_TIMEOUT)
{
break;
}
block = left;
if (block > 2048)
block = 2048;
res = net_write(s, p, block);
if ((res == 0) || (res == -56))
{
usleep(20 * 1000);
continue;
}
if (res < 0)
{
break;
}
sent += res;
left -= res;
p += res;
if ((sent / TCP_BLOCK_SIZE) > step)
{
t = gettime();
step++;
}
}
return left == 0;
}
static bool http_split_url(char **host, char **path, const char *url)
{
const char *p;
char *c;
if (strncasecmp(url, "http://", 7))
return false;
p = url + 7;
c = strchr(p, '/');
if (c[0] == 0)
return false;
*host = strndup(p, c - p);
*path = strdup(c);
return true;
}
/****************************************************************************
* http_request
* Retrieves the specified URL, and stores it in the specified file or buffer
***************************************************************************/
bool http_request(const char *url, FILE * hfile, u8 * buffer,
const u32 max_size)
{
char *http_host;
u16 http_port;
char *http_path;
http_res result;
u32 http_status;
u32 content_length = 0;
int linecount;
if (!http_split_url(&http_host, &http_path, url))
return false;
if (hfile == NULL && buffer == NULL)
return false;
http_port = 80;
http_status = 404;
int s = tcp_connect(http_host, http_port);
if (s < 0)
{
result = HTTPR_ERR_CONNECT;
return false;
}
char *request = (char *) malloc(1024);
char *r = request;
r += sprintf(r, "GET %s HTTP/1.1\r\n", http_path);
r += sprintf(r, "Host: %s\r\n", http_host);
r += sprintf(r, "Cache-Control: no-cache\r\n\r\n");
bool b = tcp_write(s, (u8 *) request, strlen(request));
free(request);
linecount = 0;
for (linecount = 0; linecount < 32; linecount++)
{
char *line = tcp_readln(s, 0xff, gettime(), (const u16) HTTP_TIMEOUT);
if (!line)
{
http_status = 404;
result = HTTPR_ERR_REQUEST;
break;
}
if (strlen(line) < 1)
{
free(line);
line = NULL;
break;
}
sscanf(line, "HTTP/1.%*u %u", &http_status);
sscanf(line, "Content-Length: %u", &content_length);
free(line);
line = NULL;
}
if (linecount == 32 || !content_length)
http_status = 404;
if (http_status != 200)
{
result = HTTPR_ERR_STATUS;
net_close(s);
return false;
}
if (content_length > max_size)
{
result = HTTPR_ERR_TOOBIG;
net_close(s);
return false;
}
int res = 1;
if (buffer != NULL)
{
b = tcp_read(s, &buffer, content_length);
}
else
{
// read into file
u32 bufSize = (1026 * 256);
u32 bytesLeft = content_length;
u32 readSize;
u8 * fbuffer = (u8 *) malloc(bufSize);
ShowProgress("Downloading...", 0, content_length);
while (bytesLeft > 0)
{
if (bytesLeft < bufSize)
readSize = bytesLeft;
else
readSize = bufSize;
b = tcp_read(s, &fbuffer, readSize);
if (!b)
break;
res = fwrite(fbuffer, 1, readSize, hfile);
if (!res)
break;
bytesLeft -= readSize;
ShowProgress("Downloading...", (content_length - bytesLeft),
content_length);
}
}
if (!b || !res)
{
result = HTTPR_ERR_RECEIVE;
net_close(s);
return false;
}
result = HTTPR_OK;
net_close(s);
return true;
}