mirror of
https://github.com/wiidev/usbloadergx.git
synced 2024-12-23 18:31:56 +01:00
*Changes to the OGG thread handling
*Changed the Updater source in some ways and added an own thread for it for later AutoUpdate feature (when gamestarting problem after networkinit is fixed)
This commit is contained in:
parent
fd4b98714f
commit
fc76b6a9c6
File diff suppressed because one or more lines are too long
@ -18,8 +18,6 @@
|
|||||||
|
|
||||||
#include "usbloader/wbfs.h"
|
#include "usbloader/wbfs.h"
|
||||||
#include "usbloader/video2.h"
|
#include "usbloader/video2.h"
|
||||||
#include "network/http.h"
|
|
||||||
#include "network/dns.h"
|
|
||||||
#include "settings/cfg.h"
|
#include "settings/cfg.h"
|
||||||
#include "language/language.h"
|
#include "language/language.h"
|
||||||
#include "mload/mload.h"
|
#include "mload/mload.h"
|
||||||
|
@ -64,7 +64,6 @@ extern FreeTypeGX *fontClock;
|
|||||||
extern u8 shutdown;
|
extern u8 shutdown;
|
||||||
extern u8 reset;
|
extern u8 reset;
|
||||||
extern int cntMissFiles;
|
extern int cntMissFiles;
|
||||||
extern int networkisinitialized;
|
|
||||||
extern struct discHdr * gameList;
|
extern struct discHdr * gameList;
|
||||||
extern u32 gameCnt;
|
extern u32 gameCnt;
|
||||||
extern s32 gameSelected, gameStart;
|
extern s32 gameSelected, gameStart;
|
||||||
@ -170,6 +169,7 @@ void ExitGUIThreads()
|
|||||||
{
|
{
|
||||||
ExitRequested = 1;
|
ExitRequested = 1;
|
||||||
LWP_JoinThread(guithread, NULL);
|
LWP_JoinThread(guithread, NULL);
|
||||||
|
guithread = LWP_THREAD_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -709,22 +709,15 @@ static int MenuDiscList()
|
|||||||
|
|
||||||
if (choice != 0)
|
if (choice != 0)
|
||||||
{
|
{
|
||||||
int netset;
|
|
||||||
int choice2 = choice;
|
int choice2 = choice;
|
||||||
|
|
||||||
netset = NetworkInitPromp(choice2);
|
SearchMissingImages(choice2);
|
||||||
|
|
||||||
if(netset < 0)
|
if(IsNetworkInit() == false)
|
||||||
{
|
{
|
||||||
WindowPrompt(LANGUAGE.Networkiniterror, 0, LANGUAGE.ok,0,0,0);
|
WindowPrompt(LANGUAGE.Networkiniterror, 0, LANGUAGE.ok,0,0,0);
|
||||||
netcheck = false;
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
netcheck = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (netcheck)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (GetMissingFiles() != NULL && cntMissFiles > 0)
|
if (GetMissingFiles() != NULL && cntMissFiles > 0)
|
||||||
|
|
||||||
@ -1872,7 +1865,7 @@ int MainMenu(int menu)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(IOS_GetVersion() != ios2 || networkisinitialized == 1) {
|
if(IOS_GetVersion() != ios2 || IsNetworkInit() == true) {
|
||||||
ret = Sys_IosReload(ios2);
|
ret = Sys_IosReload(ios2);
|
||||||
if(ret < 0) {
|
if(ret < 0) {
|
||||||
Sys_IosReload(249);
|
Sys_IosReload(249);
|
||||||
|
@ -1,133 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <ogcsys.h>
|
|
||||||
|
|
||||||
#include "http.h"
|
|
||||||
|
|
||||||
static s32 connection;
|
|
||||||
bool netcheck = false;
|
|
||||||
|
|
||||||
/*Networking - Forsaekn*/
|
|
||||||
int Net_Init(char *ip){
|
|
||||||
|
|
||||||
s32 res;
|
|
||||||
while ((res = net_init()) == -EAGAIN)
|
|
||||||
{
|
|
||||||
usleep(100 * 1000); //100ms
|
|
||||||
}
|
|
||||||
|
|
||||||
if (if_config(ip, NULL, NULL, true) < 0) {
|
|
||||||
printf(" Error reading IP address, exiting");
|
|
||||||
usleep(1000 * 1000 * 1); //1 sec
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
s32 network_request(const char * request)
|
|
||||||
{
|
|
||||||
char buf[1024];
|
|
||||||
char *ptr = NULL;
|
|
||||||
|
|
||||||
u32 cnt, size;
|
|
||||||
s32 ret;
|
|
||||||
|
|
||||||
/* Send request */
|
|
||||||
ret = net_send(connection, request, strlen(request), 0);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
/* Clear buffer */
|
|
||||||
memset(buf, 0, sizeof(buf));
|
|
||||||
|
|
||||||
/* Read HTTP header */
|
|
||||||
for (cnt = 0; !strstr(buf, "\r\n\r\n"); cnt++)
|
|
||||||
if (net_recv(connection, buf + cnt, 1, 0) <= 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
/* HTTP request OK? */
|
|
||||||
if (!strstr(buf, "HTTP/1.1 200 OK"))
|
|
||||||
return -1;
|
|
||||||
/* Retrieve content size */
|
|
||||||
ptr = strstr(buf, "Content-Length:");
|
|
||||||
if (!ptr)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
sscanf(ptr, "Content-Length: %u", &size);
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
s32 network_read(void *buf, u32 len)
|
|
||||||
{
|
|
||||||
s32 read = 0, ret;
|
|
||||||
|
|
||||||
/* Data to be read */
|
|
||||||
while (read < len) {
|
|
||||||
/* Read network data */
|
|
||||||
ret = net_read(connection, buf + read, len - read);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
/* Read finished */
|
|
||||||
if (!ret)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* Increment read variable */
|
|
||||||
read += ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return read;
|
|
||||||
}
|
|
||||||
|
|
||||||
s32 downloadrev(const char * url) {
|
|
||||||
|
|
||||||
//Check if the url starts with "http://", if not it is not considered a valid url
|
|
||||||
if(strncmp(url, "http://", strlen("http://")) != 0)
|
|
||||||
{
|
|
||||||
//printf("URL '%s' doesn't start with 'http://'\n", url);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Locate the path part of the url by searching for '/' past "http://"
|
|
||||||
char *path = strchr(url + strlen("http://"), '/');
|
|
||||||
|
|
||||||
//At the very least the url has to end with '/', ending with just a domain is invalid
|
|
||||||
if(path == NULL)
|
|
||||||
{
|
|
||||||
//printf("URL '%s' has no PATH part\n", url);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Extract the domain part out of the url
|
|
||||||
int domainlength = path - url - strlen("http://");
|
|
||||||
|
|
||||||
if(domainlength == 0)
|
|
||||||
{
|
|
||||||
//printf("No domain part in URL '%s'\n", url);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
char domain[domainlength + 1];
|
|
||||||
strncpy(domain, url + strlen("http://"), domainlength);
|
|
||||||
domain[domainlength] = '\0';
|
|
||||||
|
|
||||||
connection = GetConnection(domain);
|
|
||||||
if(connection < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Form a nice request header to send to the webserver
|
|
||||||
char* headerformat = "GET %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n";;
|
|
||||||
char header[strlen(headerformat) + strlen(domain) + strlen(path)];
|
|
||||||
sprintf(header, headerformat, path, domain);
|
|
||||||
|
|
||||||
s32 filesize = network_request(header);
|
|
||||||
|
|
||||||
return filesize;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CloseConnection() {
|
|
||||||
|
|
||||||
net_close(connection);
|
|
||||||
|
|
||||||
}
|
|
252
source/network/updater.cpp
Normal file
252
source/network/updater.cpp
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
* Updater for USB Loader GX
|
||||||
|
*
|
||||||
|
* HTTP operations
|
||||||
|
* Written by dhewg/bushing modified by dimok
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ogcsys.h>
|
||||||
|
|
||||||
|
#include "prompts/PromptWindows.h"
|
||||||
|
#include "settings/cfg.h"
|
||||||
|
#include "main.h"
|
||||||
|
#include "http.h"
|
||||||
|
|
||||||
|
static s32 connection;
|
||||||
|
static bool updatechecked = false;
|
||||||
|
static bool networkinitialized = false;
|
||||||
|
static char IP[16];
|
||||||
|
|
||||||
|
static lwp_t networkthread = LWP_THREAD_NULL;
|
||||||
|
static bool networkHalt = true;
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Initialize_Network
|
||||||
|
***************************************************************************/
|
||||||
|
void Initialize_Network(void) {
|
||||||
|
|
||||||
|
if(networkinitialized) return;
|
||||||
|
|
||||||
|
s32 result;
|
||||||
|
|
||||||
|
result = if_config(IP, NULL, NULL, true);
|
||||||
|
|
||||||
|
if(result < 0) {
|
||||||
|
networkinitialized = false;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
networkinitialized = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Check if network was initialised
|
||||||
|
***************************************************************************/
|
||||||
|
bool IsNetworkInit(void)
|
||||||
|
{
|
||||||
|
return networkinitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Get network IP
|
||||||
|
***************************************************************************/
|
||||||
|
char * GetNetworkIP(void)
|
||||||
|
{
|
||||||
|
return IP;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 network_request(const char * request)
|
||||||
|
{
|
||||||
|
char buf[1024];
|
||||||
|
char *ptr = NULL;
|
||||||
|
|
||||||
|
u32 cnt, size;
|
||||||
|
s32 ret;
|
||||||
|
|
||||||
|
/* Send request */
|
||||||
|
ret = net_send(connection, request, strlen(request), 0);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Clear buffer */
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
|
||||||
|
/* Read HTTP header */
|
||||||
|
for (cnt = 0; !strstr(buf, "\r\n\r\n"); cnt++)
|
||||||
|
if (net_recv(connection, buf + cnt, 1, 0) <= 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* HTTP request OK? */
|
||||||
|
if (!strstr(buf, "HTTP/1.1 200 OK"))
|
||||||
|
return -1;
|
||||||
|
/* Retrieve content size */
|
||||||
|
ptr = strstr(buf, "Content-Length:");
|
||||||
|
if (!ptr)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
sscanf(ptr, "Content-Length: %u", &size);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 network_read(u8 * buf, u32 len)
|
||||||
|
{
|
||||||
|
u32 read = 0;
|
||||||
|
s32 ret = -1;
|
||||||
|
|
||||||
|
/* Data to be read */
|
||||||
|
while (read < len) {
|
||||||
|
/* Read network data */
|
||||||
|
ret = net_read(connection, buf + read, len - read);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Read finished */
|
||||||
|
if (!ret)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Increment read variable */
|
||||||
|
read += ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Download request
|
||||||
|
***************************************************************************/
|
||||||
|
s32 download_request(const char * url) {
|
||||||
|
|
||||||
|
//Check if the url starts with "http://", if not it is not considered a valid url
|
||||||
|
if(strncmp(url, "http://", strlen("http://")) != 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Locate the path part of the url by searching for '/' past "http://"
|
||||||
|
char *path = strchr(url + strlen("http://"), '/');
|
||||||
|
|
||||||
|
//At the very least the url has to end with '/', ending with just a domain is invalid
|
||||||
|
if(path == NULL)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Extract the domain part out of the url
|
||||||
|
int domainlength = path - url - strlen("http://");
|
||||||
|
|
||||||
|
if(domainlength == 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char domain[domainlength + 1];
|
||||||
|
strncpy(domain, url + strlen("http://"), domainlength);
|
||||||
|
domain[domainlength] = '\0';
|
||||||
|
|
||||||
|
connection = GetConnection(domain);
|
||||||
|
if(connection < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Form a nice request header to send to the webserver
|
||||||
|
char header[strlen(path)+strlen(domain)+100];
|
||||||
|
sprintf(header, "GET %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", path, domain);
|
||||||
|
|
||||||
|
s32 filesize = network_request(header);
|
||||||
|
|
||||||
|
return filesize;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CloseConnection() {
|
||||||
|
|
||||||
|
net_close(connection);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Update check
|
||||||
|
***************************************************************************/
|
||||||
|
int CheckUpdate()
|
||||||
|
{
|
||||||
|
if(!networkinitialized)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
int revnumber = 0;
|
||||||
|
int currentrev = atoi(SVN_REV);
|
||||||
|
|
||||||
|
struct block file = downloadfile("http://www.techjawa.com/usbloadergx/rev.txt");
|
||||||
|
char revtxt[10];
|
||||||
|
|
||||||
|
u8 i;
|
||||||
|
if(file.data != NULL) {
|
||||||
|
for(i=0; i<9 || i < file.size; i++)
|
||||||
|
revtxt[i] = file.data[i];
|
||||||
|
revtxt[i] = 0;
|
||||||
|
revnumber = atoi(revtxt);
|
||||||
|
free(file.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(revnumber > currentrev)
|
||||||
|
return revnumber;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* HaltNetwork
|
||||||
|
***************************************************************************/
|
||||||
|
void HaltNetworkThread()
|
||||||
|
{
|
||||||
|
networkHalt = true;
|
||||||
|
|
||||||
|
// wait for thread to finish
|
||||||
|
while(!LWP_ThreadIsSuspended(networkthread))
|
||||||
|
usleep(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* ResumeNetworkThread
|
||||||
|
***************************************************************************/
|
||||||
|
void ResumeNetworkThread()
|
||||||
|
{
|
||||||
|
networkHalt = false;
|
||||||
|
LWP_ResumeThread(networkthread);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************************************************
|
||||||
|
* Networkthread for background network initialize and update check with idle prio
|
||||||
|
*********************************************************************************/
|
||||||
|
static void * networkinitcallback(void *arg)
|
||||||
|
{
|
||||||
|
Initialize_Network();
|
||||||
|
|
||||||
|
if(networkinitialized == true && updatechecked == false) {
|
||||||
|
if(CheckUpdate() > 0) {
|
||||||
|
/** Here we can enter the update function later **
|
||||||
|
** when network problem is solved **/
|
||||||
|
WindowPrompt("Update available",0,"OK",0,0,0);
|
||||||
|
}
|
||||||
|
updatechecked = true;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* InitNetworkThread with priority 0 (idle)
|
||||||
|
***************************************************************************/
|
||||||
|
void InitNetworkThread()
|
||||||
|
{
|
||||||
|
LWP_CreateThread (&networkthread, networkinitcallback, NULL, NULL, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* ShutdownThread
|
||||||
|
***************************************************************************/
|
||||||
|
void ShutdownNetworkThread()
|
||||||
|
{
|
||||||
|
LWP_JoinThread (networkthread, NULL);
|
||||||
|
networkthread = LWP_THREAD_NULL;
|
||||||
|
}
|
@ -1,21 +1,18 @@
|
|||||||
#ifndef _UPDATER_H_
|
#ifndef _UPDATER_H_
|
||||||
#define _UPDATER_H_
|
#define _UPDATER_H_
|
||||||
|
|
||||||
#ifdef __cplusplus
|
void Initialize_Network(void);
|
||||||
extern "C"
|
bool IsNetworkInit(void);
|
||||||
{
|
char * GetNetworkIP(void);
|
||||||
#endif
|
|
||||||
|
|
||||||
extern bool netcheck;
|
|
||||||
|
|
||||||
int Net_Init(char *ip);
|
|
||||||
s32 network_request(const char * request);
|
s32 network_request(const char * request);
|
||||||
s32 network_read(void *buf, u32 len);
|
s32 network_read(u8 *buf, u32 len);
|
||||||
s32 downloadrev(const char * url);
|
s32 download_request(const char * url);
|
||||||
void CloseConnection();
|
void CloseConnection();
|
||||||
|
int CheckUpdate();
|
||||||
|
|
||||||
#ifdef __cplusplus
|
void HaltNetworkThread();
|
||||||
}
|
void ResumeNetworkThread();
|
||||||
#endif
|
void InitNetworkThread();
|
||||||
|
void ShutdownNetworkThread();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
Copyright (c) 2008 Francisco Muñoz 'Hermes' <www.elotrolado.net>
|
Copyright (c) 2008 Francisco Muñoz 'Hermes' <www.elotrolado.net>
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
|
Threading modifications/corrections by Tantric, 2009
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification, are
|
Redistribution and use in source and binary forms, with or without modification, are
|
||||||
permitted provided that the following conditions are met:
|
permitted provided that the following conditions are met:
|
||||||
|
|
||||||
@ -28,6 +30,7 @@
|
|||||||
#include <gccore.h>
|
#include <gccore.h>
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
/* OGG control */
|
/* OGG control */
|
||||||
|
|
||||||
@ -61,15 +64,15 @@ static private_data_ogg private_ogg;
|
|||||||
#define STACKSIZE 8192
|
#define STACKSIZE 8192
|
||||||
|
|
||||||
static u8 oggplayer_stack[STACKSIZE];
|
static u8 oggplayer_stack[STACKSIZE];
|
||||||
static lwpq_t oggplayer_queue;
|
static lwpq_t oggplayer_queue = LWP_THREAD_NULL;
|
||||||
static lwp_t h_oggplayer;
|
static lwp_t h_oggplayer = LWP_THREAD_NULL;
|
||||||
static int ogg_thread_running = 0;
|
static int ogg_thread_running = 0;
|
||||||
|
|
||||||
static void ogg_add_callback(int voice)
|
static void ogg_add_callback(int voice)
|
||||||
{
|
{
|
||||||
if (ogg_thread_running <= 0)
|
if (!ogg_thread_running)
|
||||||
{
|
{
|
||||||
SND_StopVoice(0);
|
ASND_StopVoice(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,7 +81,7 @@ static void ogg_add_callback(int voice)
|
|||||||
|
|
||||||
if (private_ogg.pcm_indx >= READ_SAMPLES)
|
if (private_ogg.pcm_indx >= READ_SAMPLES)
|
||||||
{
|
{
|
||||||
if (SND_AddVoice(0,
|
if (ASND_AddVoice(0,
|
||||||
(void *) private_ogg.pcmout[private_ogg.pcmout_pos],
|
(void *) private_ogg.pcmout[private_ogg.pcmout_pos],
|
||||||
private_ogg.pcm_indx << 1) == 0)
|
private_ogg.pcm_indx << 1) == 0)
|
||||||
{
|
{
|
||||||
@ -101,6 +104,7 @@ static void ogg_add_callback(int voice)
|
|||||||
static void * ogg_player_thread(private_data_ogg * priv)
|
static void * ogg_player_thread(private_data_ogg * priv)
|
||||||
{
|
{
|
||||||
int first_time = 1;
|
int first_time = 1;
|
||||||
|
long ret;
|
||||||
|
|
||||||
ogg_thread_running = 0;
|
ogg_thread_running = 0;
|
||||||
//init
|
//init
|
||||||
@ -108,7 +112,7 @@ static void * ogg_player_thread(private_data_ogg * priv)
|
|||||||
|
|
||||||
priv[0].vi = ov_info(&priv[0].vf, -1);
|
priv[0].vi = ov_info(&priv[0].vf, -1);
|
||||||
|
|
||||||
SND_Pause(0);
|
ASND_Pause(0);
|
||||||
|
|
||||||
priv[0].pcm_indx = 0;
|
priv[0].pcm_indx = 0;
|
||||||
priv[0].pcmout_pos = 0;
|
priv[0].pcmout_pos = 0;
|
||||||
@ -118,22 +122,15 @@ static void * ogg_player_thread(private_data_ogg * priv)
|
|||||||
|
|
||||||
ogg_thread_running = 1;
|
ogg_thread_running = 1;
|
||||||
|
|
||||||
while (!priv[0].eof)
|
while (!priv[0].eof && ogg_thread_running)
|
||||||
{
|
{
|
||||||
long ret;
|
|
||||||
if (ogg_thread_running <= 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (priv[0].flag)
|
if (priv[0].flag)
|
||||||
LWP_ThreadSleep(oggplayer_queue); // wait only when i have samples to send
|
LWP_ThreadSleep(oggplayer_queue); // wait only when i have samples to send
|
||||||
|
|
||||||
if (ogg_thread_running <= 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (priv[0].flag == 0) // wait to all samples are sended
|
if (priv[0].flag == 0) // wait to all samples are sended
|
||||||
{
|
{
|
||||||
if (SND_TestPointer(0, priv[0].pcmout[priv[0].pcmout_pos])
|
if (ASND_TestPointer(0, priv[0].pcmout[priv[0].pcmout_pos])
|
||||||
&& SND_StatusVoice(0) != SND_UNUSED)
|
&& ASND_StatusVoice(0) != SND_UNUSED)
|
||||||
{
|
{
|
||||||
priv[0].flag |= 64;
|
priv[0].flag |= 64;
|
||||||
continue;
|
continue;
|
||||||
@ -188,12 +185,12 @@ static void * ogg_player_thread(private_data_ogg * priv)
|
|||||||
|
|
||||||
if (priv[0].flag == 1)
|
if (priv[0].flag == 1)
|
||||||
{
|
{
|
||||||
if (SND_StatusVoice(0) == SND_UNUSED || first_time)
|
if (ASND_StatusVoice(0) == SND_UNUSED || first_time)
|
||||||
{
|
{
|
||||||
first_time = 0;
|
first_time = 0;
|
||||||
if (priv[0].vi->channels == 2)
|
if (priv[0].vi->channels == 2)
|
||||||
{
|
{
|
||||||
SND_SetVoice(0, VOICE_STEREO_16BIT, priv[0].vi->rate, 0,
|
ASND_SetVoice(0, VOICE_STEREO_16BIT, priv[0].vi->rate, 0,
|
||||||
(void *) priv[0].pcmout[priv[0].pcmout_pos],
|
(void *) priv[0].pcmout[priv[0].pcmout_pos],
|
||||||
priv[0].pcm_indx << 1, priv[0].volume,
|
priv[0].pcm_indx << 1, priv[0].volume,
|
||||||
priv[0].volume, ogg_add_callback);
|
priv[0].volume, ogg_add_callback);
|
||||||
@ -203,7 +200,7 @@ static void * ogg_player_thread(private_data_ogg * priv)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SND_SetVoice(0, VOICE_MONO_16BIT, priv[0].vi->rate, 0,
|
ASND_SetVoice(0, VOICE_MONO_16BIT, priv[0].vi->rate, 0,
|
||||||
(void *) priv[0].pcmout[priv[0].pcmout_pos],
|
(void *) priv[0].pcmout[priv[0].pcmout_pos],
|
||||||
priv[0].pcm_indx << 1, priv[0].volume,
|
priv[0].pcm_indx << 1, priv[0].volume,
|
||||||
priv[0].volume, ogg_add_callback);
|
priv[0].volume, ogg_add_callback);
|
||||||
@ -212,13 +209,8 @@ static void * ogg_player_thread(private_data_ogg * priv)
|
|||||||
priv[0].flag = 0;
|
priv[0].flag = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
// if(priv[0].pcm_indx==0) priv[0].flag=0; // all samples sended
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
usleep(100);
|
||||||
}
|
}
|
||||||
ov_clear(&priv[0].vf);
|
ov_clear(&priv[0].vf);
|
||||||
priv[0].fd = -1;
|
priv[0].fd = -1;
|
||||||
@ -230,17 +222,20 @@ static void * ogg_player_thread(private_data_ogg * priv)
|
|||||||
|
|
||||||
void StopOgg()
|
void StopOgg()
|
||||||
{
|
{
|
||||||
SND_StopVoice(0);
|
ASND_StopVoice(0);
|
||||||
if (ogg_thread_running > 0)
|
ogg_thread_running = 0;
|
||||||
{
|
|
||||||
ogg_thread_running = -2;
|
|
||||||
LWP_ThreadSignal(oggplayer_queue);
|
|
||||||
LWP_JoinThread(h_oggplayer, NULL);
|
|
||||||
|
|
||||||
while (((volatile int) ogg_thread_running) != 0)
|
if(h_oggplayer != LWP_THREAD_NULL)
|
||||||
{
|
{
|
||||||
;;;
|
if(oggplayer_queue != LWP_TQUEUE_NULL)
|
||||||
}
|
LWP_ThreadSignal(oggplayer_queue);
|
||||||
|
LWP_JoinThread(h_oggplayer, NULL);
|
||||||
|
h_oggplayer = LWP_THREAD_NULL;
|
||||||
|
}
|
||||||
|
if(oggplayer_queue != LWP_TQUEUE_NULL)
|
||||||
|
{
|
||||||
|
LWP_CloseQueue(oggplayer_queue);
|
||||||
|
oggplayer_queue = LWP_TQUEUE_NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,8 +243,6 @@ int PlayOgg(int fd, int time_pos, int mode)
|
|||||||
{
|
{
|
||||||
StopOgg();
|
StopOgg();
|
||||||
|
|
||||||
ogg_thread_running = 0;
|
|
||||||
|
|
||||||
private_ogg.fd = fd;
|
private_ogg.fd = fd;
|
||||||
private_ogg.mode = mode;
|
private_ogg.mode = mode;
|
||||||
private_ogg.eof = 0;
|
private_ogg.eof = 0;
|
||||||
@ -269,23 +262,18 @@ int PlayOgg(int fd, int time_pos, int mode)
|
|||||||
{
|
{
|
||||||
mem_close(private_ogg.fd); // mem_close() can too close files from devices
|
mem_close(private_ogg.fd); // mem_close() can too close files from devices
|
||||||
private_ogg.fd = -1;
|
private_ogg.fd = -1;
|
||||||
ogg_thread_running = -1;
|
ogg_thread_running = 0;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LWP_CreateThread(&h_oggplayer, (void *) ogg_player_thread,
|
if (LWP_CreateThread(&h_oggplayer, (void *) ogg_player_thread,
|
||||||
&private_ogg, oggplayer_stack, STACKSIZE, 80) == -1)
|
&private_ogg, oggplayer_stack, STACKSIZE, 80) == -1)
|
||||||
{
|
{
|
||||||
ogg_thread_running = -1;
|
ogg_thread_running = 0;
|
||||||
ov_clear(&private_ogg.vf);
|
ov_clear(&private_ogg.vf);
|
||||||
private_ogg.fd = -1;
|
private_ogg.fd = -1;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
LWP_ThreadSignal(oggplayer_queue);
|
|
||||||
while (((volatile int) ogg_thread_running) == 0)
|
|
||||||
{
|
|
||||||
;;;
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,7 +330,6 @@ void PauseOgg(int pause)
|
|||||||
if (ogg_thread_running > 0)
|
if (ogg_thread_running > 0)
|
||||||
{
|
{
|
||||||
LWP_ThreadSignal(oggplayer_queue);
|
LWP_ThreadSignal(oggplayer_queue);
|
||||||
// while(((volatile int )private_ogg.flag)!=1 && ((volatile int )ogg_thread_running)>0) {;;;}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -351,31 +338,26 @@ void PauseOgg(int pause)
|
|||||||
|
|
||||||
int StatusOgg()
|
int StatusOgg()
|
||||||
{
|
{
|
||||||
if (ogg_thread_running <= 0)
|
if (ogg_thread_running == 0)
|
||||||
return -1; // Error
|
return -1; // Error
|
||||||
|
else if (private_ogg.eof)
|
||||||
if (private_ogg.eof)
|
|
||||||
return 255; // EOF
|
return 255; // EOF
|
||||||
|
else if (private_ogg.flag & 128)
|
||||||
if (private_ogg.flag & 128)
|
|
||||||
return 2; // paused
|
return 2; // paused
|
||||||
return 1; // running
|
else
|
||||||
|
return 1; // running
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetVolumeOgg(int volume)
|
void SetVolumeOgg(int volume)
|
||||||
{
|
{
|
||||||
private_ogg.volume = volume;
|
private_ogg.volume = volume;
|
||||||
SND_Pause(0);
|
ASND_ChangeVolumeVoice(0, volume, volume);
|
||||||
|
|
||||||
SND_ChangeVolumeVoice(0, volume, volume);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 GetTimeOgg()
|
s32 GetTimeOgg()
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
if (ogg_thread_running <= 0)
|
if (ogg_thread_running == 0 || private_ogg.fd < 0)
|
||||||
return 0;
|
|
||||||
if (private_ogg.fd < 0)
|
|
||||||
return 0;
|
return 0;
|
||||||
ret = ((s32) ov_time_tell(&private_ogg.vf));
|
ret = ((s32) ov_time_tell(&private_ogg.vf));
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
|
|
||||||
/*** Variables that are also used extern ***/
|
/*** Variables that are also used extern ***/
|
||||||
int cntMissFiles = 0;
|
int cntMissFiles = 0;
|
||||||
int networkisinitialized;
|
|
||||||
|
|
||||||
/*** Variables used only in this file ***/
|
/*** Variables used only in this file ***/
|
||||||
static GuiText prTxt(NULL, 26, (GXColor){THEME.prompttxt_r, THEME.prompttxt_g, THEME.prompttxt_b, 255});
|
static GuiText prTxt(NULL, 26, (GXColor){THEME.prompttxt_r, THEME.prompttxt_g, THEME.prompttxt_b, 255});
|
||||||
@ -1584,14 +1583,10 @@ FormatingPartition(const char *title, partitionEntry *entry)
|
|||||||
|
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* NetworkInit
|
* SearchMissingImages
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
int NetworkInitPromp(int choice2)
|
void SearchMissingImages(int choice2)
|
||||||
{
|
{
|
||||||
char hostip[16];
|
|
||||||
char * IP = NULL;
|
|
||||||
s32 ret = -1;
|
|
||||||
|
|
||||||
GuiWindow promptWindow(472,320);
|
GuiWindow promptWindow(472,320);
|
||||||
promptWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE);
|
promptWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE);
|
||||||
promptWindow.SetPosition(0, -10);
|
promptWindow.SetPosition(0, -10);
|
||||||
@ -1649,23 +1644,24 @@ int NetworkInitPromp(int choice2)
|
|||||||
|
|
||||||
ResumeGui();
|
ResumeGui();
|
||||||
|
|
||||||
while (!IP)
|
while (!IsNetworkInit()) {
|
||||||
{
|
|
||||||
|
|
||||||
VIDEO_WaitVSync();
|
VIDEO_WaitVSync();
|
||||||
|
|
||||||
ret = Net_Init(hostip);
|
Initialize_Network();
|
||||||
|
|
||||||
if (ret > 0) {
|
if (!IsNetworkInit()) {
|
||||||
IP = hostip;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret <= 0) {
|
|
||||||
msgTxt.SetText(LANGUAGE.Couldnotinitializenetwork);
|
msgTxt.SetText(LANGUAGE.Couldnotinitializenetwork);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IP && ret > 0) {
|
if(btn1.GetState() == STATE_CLICKED) {
|
||||||
msgTxt.SetTextf("IP: %s", IP);
|
btn1.ResetState();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsNetworkInit()) {
|
||||||
|
msgTxt.SetTextf("IP: %s", GetNetworkIP());
|
||||||
cntMissFiles = 0;
|
cntMissFiles = 0;
|
||||||
u32 i = 0;
|
u32 i = 0;
|
||||||
char filename[11];
|
char filename[11];
|
||||||
@ -1701,16 +1697,8 @@ int NetworkInitPromp(int choice2)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(btn1.GetState() == STATE_CLICKED) {
|
|
||||||
IP = 0;
|
|
||||||
ret = -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
promptWindow.SetEffect(EFFECT_SLIDE_TOP | EFFECT_SLIDE_OUT, 50);
|
promptWindow.SetEffect(EFFECT_SLIDE_TOP | EFFECT_SLIDE_OUT, 50);
|
||||||
while(promptWindow.GetEffect() > 0) usleep(50);
|
while(promptWindow.GetEffect() > 0) usleep(50);
|
||||||
HaltGui();
|
HaltGui();
|
||||||
@ -1718,9 +1706,7 @@ int NetworkInitPromp(int choice2)
|
|||||||
mainWindow->SetState(STATE_DEFAULT);
|
mainWindow->SetState(STATE_DEFAULT);
|
||||||
ResumeGui();
|
ResumeGui();
|
||||||
|
|
||||||
networkisinitialized = 1;
|
return;
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
@ -2139,15 +2125,11 @@ ProgressDownloadWindow(int choice2)
|
|||||||
* progress bar showing % completion, or a throbber that only shows that an
|
* progress bar showing % completion, or a throbber that only shows that an
|
||||||
* action is in progress.
|
* action is in progress.
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
int
|
#define BLOCKSIZE 1024
|
||||||
ProgressUpdateWindow()
|
|
||||||
{
|
|
||||||
|
|
||||||
|
int ProgressUpdateWindow()
|
||||||
|
{
|
||||||
int ret = 0, failed = 0;
|
int ret = 0, failed = 0;
|
||||||
const unsigned int blocksize = 1024;
|
|
||||||
char hostip[16];
|
|
||||||
char * IP = NULL;
|
|
||||||
u8 blockbuffer[blocksize] ATTRIBUTE_ALIGN(32);
|
|
||||||
|
|
||||||
GuiWindow promptWindow(472,320);
|
GuiWindow promptWindow(472,320);
|
||||||
promptWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE);
|
promptWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE);
|
||||||
@ -2255,23 +2237,19 @@ ProgressUpdateWindow()
|
|||||||
snprintf(dolpath, sizeof(dolpath), "%sbootnew.dol", Settings.update_path);
|
snprintf(dolpath, sizeof(dolpath), "%sbootnew.dol", Settings.update_path);
|
||||||
snprintf(dolpathsuccess, sizeof(dolpathsuccess), "%sboot.dol", Settings.update_path);
|
snprintf(dolpathsuccess, sizeof(dolpathsuccess), "%sboot.dol", Settings.update_path);
|
||||||
|
|
||||||
while (!IP && !(ret < 0)) {
|
while (!IsNetworkInit()) {
|
||||||
|
|
||||||
VIDEO_WaitVSync();
|
VIDEO_WaitVSync();
|
||||||
|
|
||||||
ret = Net_Init(hostip);
|
Initialize_Network();
|
||||||
|
|
||||||
if (ret > 0) {
|
if (IsNetworkInit()) {
|
||||||
IP = hostip;
|
msgTxt.SetText(GetNetworkIP());
|
||||||
msgTxt.SetText(IP);
|
} else {
|
||||||
}
|
|
||||||
|
|
||||||
if (ret <= 0) {
|
|
||||||
msgTxt.SetText(LANGUAGE.Couldnotinitializenetwork);
|
msgTxt.SetText(LANGUAGE.Couldnotinitializenetwork);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(btn1.GetState() == STATE_CLICKED) {
|
if(btn1.GetState() == STATE_CLICKED) {
|
||||||
IP = 0;
|
|
||||||
ret = -1;
|
ret = -1;
|
||||||
failed = -1;
|
failed = -1;
|
||||||
btn1.ResetState();
|
btn1.ResetState();
|
||||||
@ -2279,30 +2257,13 @@ ProgressUpdateWindow()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(IP && ret >= 0) {
|
if(IsNetworkInit() && ret >= 0) {
|
||||||
|
|
||||||
networkisinitialized = 1;
|
int newrev = CheckUpdate();
|
||||||
|
|
||||||
int revnumber = 0;
|
if(newrev > 0) {
|
||||||
int currentrev = atoi(SVN_REV);
|
|
||||||
|
|
||||||
/// SDCard_deInit();
|
sprintf(msg, "Rev%i %s.", newrev, LANGUAGE.available);
|
||||||
struct block file = downloadfile("http://www.techjawa.com/usbloadergx/rev.txt");
|
|
||||||
FILE *pfile;
|
|
||||||
/// SDCard_Init();
|
|
||||||
|
|
||||||
if(file.data != NULL)
|
|
||||||
{
|
|
||||||
char revtxt[10];
|
|
||||||
u8 i;
|
|
||||||
for(i=0; i<9 || i<file.size; i++)
|
|
||||||
revtxt[i] = file.data[i];
|
|
||||||
revtxt[i] = 0;
|
|
||||||
revnumber = atoi(revtxt);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(revnumber > currentrev) {
|
|
||||||
sprintf(msg, "Rev%i %s.", revnumber, LANGUAGE.available);
|
|
||||||
int choice = WindowPrompt(msg, LANGUAGE.Doyouwanttoupdate, LANGUAGE.Updatedol, LANGUAGE.Updateall, LANGUAGE.Cancel, 0);
|
int choice = WindowPrompt(msg, LANGUAGE.Doyouwanttoupdate, LANGUAGE.Updatedol, LANGUAGE.Updateall, LANGUAGE.Cancel, 0);
|
||||||
if(choice == 1 || choice == 2) {
|
if(choice == 1 || choice == 2) {
|
||||||
titleTxt.SetTextf("%s USB Loader GX", LANGUAGE.updating);
|
titleTxt.SetTextf("%s USB Loader GX", LANGUAGE.updating);
|
||||||
@ -2311,13 +2272,15 @@ ProgressUpdateWindow()
|
|||||||
promptWindow.Append(&progressbarImg);
|
promptWindow.Append(&progressbarImg);
|
||||||
promptWindow.Append(&progressbarOutlineImg);
|
promptWindow.Append(&progressbarOutlineImg);
|
||||||
promptWindow.Append(&prTxt);
|
promptWindow.Append(&prTxt);
|
||||||
msgTxt.SetTextf("%s Rev%i", LANGUAGE.Updateto, revnumber);
|
msgTxt.SetTextf("%s Rev%i", LANGUAGE.Updateto, newrev);
|
||||||
int filesize = downloadrev("http://www.techjawa.com/usbloadergx/boot.dol");
|
s32 filesize = download_request("http://www.techjawa.com/usbloadergx/boot.dol");
|
||||||
if(filesize > 0) {
|
if(filesize > 0) {
|
||||||
|
FILE * pfile;
|
||||||
pfile = fopen(dolpath, "wb");
|
pfile = fopen(dolpath, "wb");
|
||||||
for (int i = 0; i < filesize; i += blocksize) {
|
u8 blockbuffer[BLOCKSIZE] ATTRIBUTE_ALIGN(32);
|
||||||
|
for (s32 i = 0; i < filesize; i += BLOCKSIZE) {
|
||||||
prTxt.SetTextf("%i%%", 100*i/filesize);
|
prTxt.SetTextf("%i%%", 100*i/filesize);
|
||||||
if ((Settings.wsprompt == yes) && (CFG.widescreen)){/////////////adjust for widescreen
|
if ((Settings.wsprompt == yes) && (CFG.widescreen)) {
|
||||||
progressbarImg.SetTile(80*i/filesize);
|
progressbarImg.SetTile(80*i/filesize);
|
||||||
} else {
|
} else {
|
||||||
progressbarImg.SetTile(100*i/filesize);
|
progressbarImg.SetTile(100*i/filesize);
|
||||||
@ -2334,11 +2297,11 @@ ProgressUpdateWindow()
|
|||||||
|
|
||||||
u32 blksize;
|
u32 blksize;
|
||||||
blksize = (u32)(filesize - i);
|
blksize = (u32)(filesize - i);
|
||||||
if (blksize > blocksize)
|
if (blksize > BLOCKSIZE)
|
||||||
blksize = blocksize;
|
blksize = BLOCKSIZE;
|
||||||
|
|
||||||
ret = network_read(blockbuffer, blksize);
|
ret = network_read(blockbuffer, blksize);
|
||||||
if ((u32)ret != blksize) {
|
if (ret != (s32) blksize) {
|
||||||
failed = -1;
|
failed = -1;
|
||||||
ret = -1;
|
ret = -1;
|
||||||
fclose(pfile);
|
fclose(pfile);
|
||||||
@ -2359,7 +2322,7 @@ ProgressUpdateWindow()
|
|||||||
if(choice == 2) {
|
if(choice == 2) {
|
||||||
//get the icon.png and the meta.xml
|
//get the icon.png and the meta.xml
|
||||||
char xmliconpath[150];
|
char xmliconpath[150];
|
||||||
file = downloadfile("http://www.techjawa.com/usbloadergx/meta.file");
|
struct block file = downloadfile("http://www.techjawa.com/usbloadergx/meta.file");
|
||||||
if(file.data != NULL){
|
if(file.data != NULL){
|
||||||
sprintf(xmliconpath, "%smeta.xml", Settings.update_path);
|
sprintf(xmliconpath, "%smeta.xml", Settings.update_path);
|
||||||
pfile = fopen(xmliconpath, "wb");
|
pfile = fopen(xmliconpath, "wb");
|
||||||
|
@ -17,7 +17,7 @@ int WindowExitPrompt(const char *title, const char *msg, const char *btn1Label,
|
|||||||
int GameWindowPrompt();
|
int GameWindowPrompt();
|
||||||
int DiscWait(const char *title, const char *msg, const char *btn1Label, const char *btn2Label, int IsDeviceWait);
|
int DiscWait(const char *title, const char *msg, const char *btn1Label, const char *btn2Label, int IsDeviceWait);
|
||||||
int FormatingPartition(const char *title, partitionEntry *entry);
|
int FormatingPartition(const char *title, partitionEntry *entry);
|
||||||
int NetworkInitPromp(int choice2);
|
void SearchMissingImages(int choice2);
|
||||||
int ProgressWindow(const char *title, const char *msg);
|
int ProgressWindow(const char *title, const char *msg);
|
||||||
int ProgressDownloadWindow(int choice2);
|
int ProgressDownloadWindow(int choice2);
|
||||||
int ProgressUpdateWindow();
|
int ProgressUpdateWindow();
|
||||||
|
Loading…
Reference in New Issue
Block a user