/* $Id$ */
/** @file newgrf_download.cpp GUI to download NewGRFs. */
#include "../stdafx.h"
#include "../openttd.h"
#include "../strings_func.h"
#include "../date_func.h"
#include "../gui.h"
#include "../window_gui.h"
#include "../textbuf_gui.h"
#include "../variables.h"
#include "../newgrf.h"
#include "newgrf_download.h"
#include "core/config.h"
#include "core/game.h"
#include "../functions.h"
#include "../debug.h"
#include "../window_func.h"
#include "../core/alloc_func.hpp"
#include "../string_func.h"
#include "../gfx_func.h"
#include "../querystring_gui.h"
#include "../sortlist_type.h"
#include "../fileio.h"
#include "../rev.h"
// XXX: ...
#include <curl/curl.h>
#include "table/strings.h"
#include "../table/sprites.h"
static int _has_curl_init = 0;
static CURLcode do_init_curl () {
CURLcode err;
if (!_has_curl_init) {
// XXX: be smarter about what to initialize
if ((err = curl_global_init(CURL_GLOBAL_ALL)))
return err;
_has_curl_init = 1;
}
return CURLE_OK;
}
struct check_write_ctx {
char *buf;
size_t len;
size_t offset;
};
#define QUERY_ITEM_SIZE ( \
4 /* the "grf=" prefix */ \
+ 8 /* the GRFID, in hex */ \
+ 1 /* the ":" */ \
+ 32 /* the md5sum, in hex */ \
+ 1 /* the "&" or '\0' */ \
)
static size_t check_available_write_cb (void *ptr, size_t size, size_t nmemb, void *arg) {
struct check_write_ctx *ctx = (struct check_write_ctx *) arg;
// total number of bytes
size_t len = size * nmemb, oldlen = ctx->len;
// increase the size until the new data fits
while (ctx->offset + len + 1 > ctx->len) {
ctx->len *= 2;
}
// realloc if needed
if (ctx->len != oldlen)
ctx->buf = ReallocT<char>(ctx->buf, ctx->len);
// thankfully we only have a limited amount of data..
memcpy(ctx->buf + ctx->offset, ptr, len);
ctx->offset += len;
*(ctx->buf + ctx->offset) = '\0';
return len;
}
/** Ask the master database about the availablity of the given NewGRFs, and update
* the GRFConfigs that are avilable to GCS_AVAILABLE
* @param config pointer to a linked-list of grfconfig's needed */
static int CheckAvailableNewGRFs(GRFConfig *list)
{
GRFConfig *c;
int i;
char md5buf[64];
struct check_write_ctx write_ctx;
CURL *curl;
char api_url[512], useragent[512];
char curl_errbuf[CURL_ERROR_SIZE];
long http_code;
char *buf_ptr;
CURLcode err;
int ret = -1;
int found = 0;
// initialize to a safe state for error handling
curl = write_ctx.buf = NULL;
// count how many NewGRFs in our list
for (c = list, i = 0; c != NULL; c = c->next, i++) {}
// how many bytes these take up...
int query_size = i * QUERY_ITEM_SIZE;
// allocate the buffer for the query data
char *query_buf = MallocT<char>(query_size), *query_ptr = query_buf;
// build the query
for (c = list; c != NULL; c = c->next) {
// the "&", except not for the first entry
if (c != list)
*(query_ptr++) = '&';
// format the md5sum
md5sumToString(md5buf, lastof(md5buf), c->md5sum);
// snprintf the entry into the query buffer
assert(snprintf(query_ptr, QUERY_ITEM_SIZE, "grf=%08X:%32s", BSWAP32(c->grfid), md5buf) == QUERY_ITEM_SIZE -1);
// advance the query_ptr to point at the nul byte
query_ptr += QUERY_ITEM_SIZE - 1;
}
// buil the target URL by formatting NETWORK_NEWGRF_MASTER_API with the method as the only parameter
assert(snprintf(api_url, sizeof(api_url), NETWORK_NEWGRF_MASTER_API, "search") < 512);
// our useragent
assert(snprintf(useragent, sizeof(useragent), NETWORK_HTTP_USER_AGENT, _openttd_revision) < 512);
// receiving is so damn hard...
write_ctx.buf = MallocT<char>(512);
write_ctx.len = 512;
write_ctx.offset = 0;
// set up curl globally if needed
if ((err = do_init_curl())) {
DEBUG(grfdl, 0, "curl_global_init failed: %s", curl_easy_strerror(err));
goto error;
}
// allocate a new curl_easy
if ((curl = curl_easy_init()) == NULL) {
DEBUG(grfdl, 0, "curl_easy_init failed");
goto error;
}
// let's set some options...
if (
(CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_VERBOSE, 1))) // XXX: tie this in with OpenTTD's debug stuff
/*
curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, XXX)
*/
|| (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1))) // no progress meter on stdout, thx
|| (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf))) // XXX: human-readable error messages
|| (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_URL, api_url))) // the URL to retrieve
|| (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_USERAGENT, useragent))) // the User-agent to use
|| (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, query_buf))) // the POST data
|| (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &check_available_write_cb)))
|| (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write_ctx))) // the write buffer
) {
DEBUG(grfdl, 0, "curl_easy_setopt failed: %s", curl_easy_strerror(err));
goto error;
}
// XXX: currently this freezes the UI..
if ((err = curl_easy_perform(curl))) {
DEBUG(grfdl, 0, "the curl request failed: %s", curl_easy_strerror(err));
goto error;
}
// get the HTTP return code
if ((err = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code))) {
DEBUG(grfdl, 0, "curl_easy_getinfo failed: %s", curl_easy_strerror(err));
goto error;
}
if (http_code != 200) {
DEBUG(grfdl, 0, "Bad HTTP response code: %d", http_code);
goto error;
}
// start parsing...
buf_ptr = write_ctx.buf;
while (buf_ptr < write_ctx.buf + write_ctx.offset) {
// replace the \n with a \0
char *nl = strchr(buf_ptr, '\n');
if (!nl) {
DEBUG(grfdl, 0, "Incomplete line in search response: %s", buf_ptr);
goto error;
}
*nl = '\0';
// check the format
if (buf_ptr[8] != ':' || buf_ptr[8 + 1 + 32] != ':') {
DEBUG(grfdl, 0, "Invalid line in search response: %s", buf_ptr);
goto error;
}
// separate out the fields
buf_ptr[8] = buf_ptr[8 + 1 + 32] = '\0';
// parse the GRFID...
char *invalid;
uint32 grfid = strtol(buf_ptr + 0, &invalid, 16);
if (*invalid) {
DEBUG(grfdl, 0, "Invalid GRFID in search response: %s", buf_ptr);
goto error;
}
// parse the md5sum
uint8 md5sum[16];
if (StringToMd5sum(md5sum, buf_ptr + 8 + 1)) {
DEBUG(grfdl, 0, "Invalid md5sum in search response: %s", buf_ptr);
goto error;
}
// copy the URL
char *url = strdup(buf_ptr + 8 + 1 + 32 + 1);
// look for the GRF
for (c = list; c != NULL; c = c->next) {
if (BSWAP32(c->grfid) == grfid && memcmp(c->md5sum, md5sum, 16) == 0) {
DEBUG(grfdl, 0, "Found %08X:%s at %s", buf_ptr + 0, buf_ptr + 8 + 1, url);
c->filename = url;
c->status = GCS_AVAILABLE;
ClrBit(c->status, GCF_COMPATIBLE);
found++;
break;
}
}
// free the allocated memory if it wasn't stored in a GRFConfig
if (c == NULL) {
free(url);
}
// move on to the next line
buf_ptr = nl + 1;
}
// return number of NewGRFs found, defaults to -1 for errors
ret = found;
error :
// cleanup
free(write_ctx.buf);
if (curl)
curl_easy_cleanup(curl);
// return as defined by ret
return ret;
}
/** Download the NewGRFs that are listed as GCS_AVAILABLE
* @param config pointer to a linked-list of grfconfig's to be downloaded */
static int DownloadAvailableNewGRFs(GRFConfig *list)
{
GRFConfig *c;
CURL *curl = NULL;
CURLcode err;
FILE *fh = NULL;
char md5buf[64], filename[256], useragent[512];
long http_code;
int ret = -1;
int found = 0;
// set up curl
if ((err = do_init_curl())) {
DEBUG(grfdl, 0, "curl_global_init failed: %s", curl_easy_strerror(err));
goto error;
}
// allocate a new curl_easy
if ((curl = curl_easy_init()) == NULL) {
DEBUG(grfdl, 0, "curl_easy_init failed");
goto error;
}
// our useragent
assert(snprintf(useragent, sizeof(useragent), NETWORK_HTTP_USER_AGENT, _openttd_revision) < 512);
// let's set some options...
if (
(CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_VERBOSE, 1))) // XXX: tie this in with OpenTTD's debug stuff
/*
curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, XXX)
*/
|| (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1))) // no progress meter on stdout, thx
// || (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf))) // human-readable error messages
|| (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_USERAGENT, useragent))) // the User-agent to use
/*
|| (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, query_buf))) // the POST data
|| (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &check_available_write_cb)))
*/
) {
DEBUG(grfdl, 0, "curl_easy_setopt failed: %s", curl_easy_strerror(err));
goto error;
}
// then start looping through the NewGRFs to download
for (c = list; c != NULL; c = c->next) {
// skip the ones that are not available
if (c->status != GCS_AVAILABLE)
continue;
// format the md5sum
md5sumToString(md5buf, lastof(md5buf), c->md5sum);
// snprintf the entry into the query buffer
assert(snprintf(filename, sizeof(filename), "%08X-%32s.tar", BSWAP32(c->grfid), md5buf) < 256);
// open the file
if ((fh = FioFOpenFile(filename, "wb", CACHE_DIR)) == NULL) {
DEBUG(grfdl, 0, "Opening local GRF file failed: %s", filename);
goto error;
}
// set up curl
if (
(CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_URL, c->filename))) // the URL to retrieve
|| (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_WRITEDATA, fh))) // XXX: need to fwrite() ourself
) {
DEBUG(grfdl, 0, "curl_easy_setopt failed: %s", curl_easy_strerror(err));
goto error;
}
// XXX: currently this freezes the UI..
if ((err = curl_easy_perform(curl))) {
DEBUG(grfdl, 0, "the curl request failed: %s", curl_easy_strerror(err));
goto error;
}
// get the HTTP return code
if ((err = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code))) {
DEBUG(grfdl, 0, "curl_easy_getinfo failed: %s", curl_easy_strerror(err));
goto error;
}
if (http_code != 200) {
DEBUG(grfdl, 0, "Bad HTTP response code: %d", http_code);
goto error;
}
// OK, great
c->status = GCS_UNKNOWN;
ClrBit(c->status, GCF_COMPATIBLE);
found++;
}
// success
ret = found;
error:
if (fh)
fclose(fh);
if (curl)
curl_easy_cleanup(curl);
return ret;
}
// XXX: copy-pasted from newgrf_gui.cpp
static void ShowDownloadNewGRFInfo(const GRFConfig *c, uint x, uint y, uint w, uint bottom, bool show_params)
{
char buff[256];
if (c->error != NULL) {
char message[512];
if (c->error->custom_message == NULL) {
SetDParamStr(0, c->filename);
SetDParamStr(1, c->error->data);
for (uint i = 0; i < c->error->num_params; i++) {
uint32 param = 0;
byte param_number = c->error->param_number[i];
if (param_number < c->num_params) param = c->param[param_number];
SetDParam(2 + i, param);
}
GetString(message, c->error->message, lastof(message));
} else {
SetDParamStr(0, c->error->custom_message);
GetString(message, STR_JUST_RAW_STRING, lastof(message));
}
SetDParamStr(0, message);
y += DrawStringMultiLine(x, y, c->error->severity, w, bottom - y);
}
/* Draw filename or not if it is not known (GRF sent over internet) */
if (c->filename != NULL) {
SetDParamStr(0, c->filename);
y += DrawStringMultiLine(x, y, STR_NEWGRF_FILENAME, w, bottom - y);
}
/* Prepare and draw GRF ID */
snprintf(buff, lengthof(buff), "%08X", BSWAP32(c->grfid));
SetDParamStr(0, buff);
y += DrawStringMultiLine(x, y, STR_NEWGRF_GRF_ID, w, bottom - y);
/* Prepare and draw MD5 sum */
md5sumToString(buff, lastof(buff), c->md5sum);
SetDParamStr(0, buff);
y += DrawStringMultiLine(x, y, STR_NEWGRF_MD5SUM, w, bottom - y);
/* Show GRF parameter list */
if (show_params) {
if (c->num_params > 0) {
GRFBuildParamList(buff, c, lastof(buff));
SetDParam(0, STR_JUST_RAW_STRING);
SetDParamStr(1, buff);
} else {
SetDParam(0, STR_01A9_NONE);
}
y += DrawStringMultiLine(x, y, STR_NEWGRF_PARAMETER, w, bottom - y);
}
/* Show flags */
if (c->status == GCS_NOT_FOUND) y += DrawStringMultiLine(x, y, STR_NEWGRF_NOT_FOUND, w, bottom - y);
if (c->status == GCS_DISABLED) y += DrawStringMultiLine(x, y, STR_NEWGRF_DISABLED, w, bottom - y);
if (HasBit(c->flags, GCF_COMPATIBLE)) y += DrawStringMultiLine(x, y, STR_NEWGRF_COMPATIBLE_LOADED, w, bottom - y);
/* Draw GRF info if it exists */
if (c->info != NULL && !StrEmpty(c->info)) {
SetDParam(0, STR_JUST_RAW_STRING);
SetDParamStr(1, c->info);
y += DrawStringMultiLine(x, y, STR_02BD, w, bottom - y);
} else {
y += DrawStringMultiLine(x, y, STR_NEWGRF_NO_INFO, w, bottom - y);
}
}
/**
* Window for downloading NewGRF files
*/
struct NewGRFDownloadWindow : public Window {
/* Names of the add a newgrf window widgets */
enum DownloadNewGRFWindowWidgets {
DNGRFS_CLOSEBOX = 0,
DNGRFS_CAPTION,
DNGRFS_BACKGROUND,
DNGRFS_FILE_LIST,
DNGRFS_SCROLLBAR,
DNGRFS_NEWGRF_INFO,
DNGRFS_CHECK_AVAILABLE,
DNGRFS_DOWNLOAD_AVAILABLE,
DNGRFS_RESIZE,
};
struct NetworkGameInfo *info; ///< the server we are downloading NewGRFs for
GRFConfig *list; ///< temporary grf list to which changes are made
const GRFConfig *sel;
NewGRFDownloadWindow(const WindowDesc *desc, struct NetworkGameInfo *info, GRFConfig *list) : Window(desc, 0)
{
// resize magic
this->resize.step_height = 14;
// the server info
this->info = info;
// the list of GRFs that we need
this->list = list;
// display window?
this->FindWindowPlacementAndResize(desc);
// set up our own state
this->SetupDownloadNewGRFWindow();
}
~NewGRFDownloadWindow()
{
// Apply changes from list to the config
GRFConfig *config, *list;
// default to true, set back to false if we find a still-missing NewGRF
this->info->compatible = true;
for (config = this->info->grfconfig, list = this->list; config != NULL && list != NULL; config = config->next, list = list->next) {
// skip those entries that aren't in list
if (config->grfid == list->grfid) {
// changed?
if (config->status != list->status && list->status != GCS_AVAILABLE) {
config->status = list->status;
ClrBit(config->flags, GCF_COMPATIBLE);
}
}
// is this still NOT_FOUND?
if (config->status == GCS_NOT_FOUND)
this->info->compatible = false;
}
/* Remove the temporary copy of grf-list used in window */
ClearGRFConfigList(&this->list);
}
void SetupDownloadNewGRFWindow()
{
const GRFConfig *c;
int i;
// count how many NewGRFs in our list
for (c = this->list, i = 0; c != NULL; c = c->next, i++) {}
// something to do with vertical scrolling...
this->vscroll.cap = (this->widget[DNGRFS_FILE_LIST].bottom - this->widget[DNGRFS_FILE_LIST].top) / 14 + 1;
SetVScrollCount(this, i);
// disable the "Check Available" button if there are no incompatible NewGRFs
this->SetWidgetDisabledState(DNGRFS_CHECK_AVAILABLE, (i == 0));
// disable the "Download Available" button until we've checked for available NewGRFs
this->SetWidgetDisabledState(DNGRFS_DOWNLOAD_AVAILABLE, 1);
}
virtual void OnPaint()
{
// superclass widget-drawing
this->DrawWidgets();
/* Draw the NewGRF file list */
int y = this->widget[DNGRFS_FILE_LIST].top;
int i = 0;
// for each GRF...
for (const GRFConfig *c = this->list; c != NULL; c = c->next, i++) {
// is scrolled into view
if (i >= this->vscroll.pos && i < this->vscroll.pos + this->vscroll.cap) {
SpriteID pal;
byte txtoffset;
// the NewGRF name
const char *text = (c->name != NULL && !StrEmpty(c->name)) ? c->name : c->filename;
/*
* Pick a colour
*
* orange - server has it, we don't, haven't checked availablity yet
* red - not available
* green - available
* blue - downloaded
*/
switch (c->status) {
case GCS_NOT_FOUND:
pal = PALETTE_TO_ORANGE;
break;
case GCS_DISABLED:
pal = PALETTE_TO_RED;
break;
case GCS_AVAILABLE:
pal = PALETTE_TO_GREEN;
break;
default: // GCS_INITIALIZED or GCS_ACTIVATED
pal = PALETTE_TO_BLUE;
break;
}
/*
XXX: flags?
if (pal != PALETTE_TO_RED) {
if (HasBit(c->flags, GCF_STATIC)) {
pal = PALETTE_TO_GREY;
} else if (HasBit(c->flags, GCF_COMPATIBLE)) {
pal = PALETTE_TO_ORANGE;
}
}
*/
// draw the square in the chosen colour
DrawSprite(SPR_SQUARE, pal, 5, y + 2);
// XXX: hmm... use for download errors?
if (c->error != NULL) DrawSprite(SPR_WARNING_SIGN, 0, 20, y + 2);
// where to draw the text
txtoffset = c->error != NULL ? 35 : 25;
// draw the chosen text (GRF name/filename)
DoDrawStringTruncated(text, txtoffset, y + 3, this->sel == c ? TC_WHITE : TC_BLACK, this->width - txtoffset - 10);
// advance on to the next row
y += 14;
}
}
if (this->sel != NULL) {
/* Draw NewGRF file info */
const Widget *wi = &this->widget[DNGRFS_NEWGRF_INFO];
ShowDownloadNewGRFInfo(this->sel, wi->left + 2, wi->top + 2, wi->right - wi->left - 2, wi->bottom, false);
}
}
virtual void OnClick(Point pt, int widget)
{
int found;
switch (widget) {
case DNGRFS_CHECK_AVAILABLE:
// do the HTTP request etc.
found = CheckAvailableNewGRFs(this->list);
if (found < 0) {
// failed
ShowErrorMessage(INVALID_STRING_ID, STR_NEWGRF_DOWNLOAD_CHECK_FAILED, 0, 0);
} else if (found == 0) {
// no NewGRFs found
ShowErrorMessage(INVALID_STRING_ID, STR_NEWGRF_DOWNLOAD_CHECK_NONE, 0, 0);
} else {
// success, flip the button enabled states
this->SetWidgetDisabledState(DNGRFS_CHECK_AVAILABLE, 1);
this->SetWidgetDisabledState(DNGRFS_DOWNLOAD_AVAILABLE, 0);
// redraw with new status
this->SetDirty();
}
break;
case DNGRFS_DOWNLOAD_AVAILABLE:
// do a bunch of HTTP requests
found = DownloadAvailableNewGRFs(this->list);
if (found < 0) {
// failed
ShowErrorMessage(INVALID_STRING_ID, STR_NEWGRF_DOWNLOAD_FAILED, 0, 0);
} else {
if (found == 0) {
// ???
// this shouldn't really happen, as we check for this in CHECK_AVAILABLE
ShowErrorMessage(INVALID_STRING_ID, STR_NEWGRF_DOWNLOAD_CHECK_NONE, 0, 0);
} else {
/* XXX: not exported, so prototype here */
void ScanForTarFiles ();
// load the newly downloaded NewGRF files
ScanForTarFiles();
ScanNewGRFFiles();
}
// disable the button, not useful anymore
this->SetWidgetDisabledState(DNGRFS_DOWNLOAD_AVAILABLE, 1);
// redraw with new status
this->SetDirty();
}
break;
case DNGRFS_FILE_LIST: { // Select a GRF
GRFConfig *c;
uint i = (pt.y - this->widget[DNGRFS_FILE_LIST].top) / 14 + this->vscroll.pos;
for (c = this->list; c != NULL && i > 0; c = c->next, i--) {}
this->sel = c;
this->SetDirty();
break;
}
}
}
virtual void OnResize(Point new_size, Point delta)
{
if (delta.x != 0) {
ResizeButtons(this, DNGRFS_CHECK_AVAILABLE, DNGRFS_DOWNLOAD_AVAILABLE);
}
this->vscroll.cap += delta.y / 14;
this->widget[DNGRFS_FILE_LIST].data = (this->vscroll.cap << 8) + 1;
this->SetupDownloadNewGRFWindow();
}
virtual void OnInvalidateData(int data = 0)
{
this->SetupDownloadNewGRFWindow();
}
};
/* Widget definition of the download newgrfs window */
static const Widget _newgrf_download_widgets[] = {
{ WWT_CLOSEBOX, RESIZE_NONE, 10, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW }, // DNGRFS_CLOSEBOX
{ WWT_CAPTION, RESIZE_RIGHT, 10, 11, 299, 0, 13, STR_NEWGRF_DOWNLOAD_CAPTION, STR_018C_WINDOW_TITLE_DRAG_THIS }, // DNGRFS_CAPTION
{ WWT_PANEL, RESIZE_RIGHT, 10, 0, 299, 14, 29, STR_NULL, STR_NULL }, // DNGRFS_BACKGROUND
/*
{ WWT_PUSHTXTBTN, RESIZE_NONE, 3, 10, 79, 16, 27, STR_NEWGRF_ADD, STR_NEWGRF_ADD_TIP }, // SNGRFS_ADD
{ WWT_PUSHTXTBTN, RESIZE_NONE, 3, 80, 149, 16, 27, STR_NEWGRF_REMOVE, STR_NEWGRF_REMOVE_TIP }, // SNGRFS_REMOVE
{ WWT_PUSHTXTBTN, RESIZE_NONE, 3, 150, 219, 16, 27, STR_NEWGRF_MOVEUP, STR_NEWGRF_MOVEUP_TIP }, // SNGRFS_MOVE_UP
{ WWT_PUSHTXTBTN, RESIZE_RIGHT, 3, 220, 289, 16, 27, STR_NEWGRF_MOVEDOWN, STR_NEWGRF_MOVEDOWN_TIP }, // SNGRFS_MOVE_DOWN
*/
{ WWT_MATRIX, RESIZE_RB, 10, 0, 287, 30, 99, 0x501, STR_NEWGRF_DOWNLOAD_LIST_TIP }, // DNGRFS_FILE_LIST
{ WWT_SCROLLBAR, RESIZE_LRB, 10, 288, 299, 30, 99, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST }, // DNGRFS_SCROLLBAR
{ WWT_PANEL, RESIZE_RTB, 10, 0, 299, 100, 212, STR_NULL, STR_NULL }, // DNGRFS_NEWGRF_INFO
{ WWT_PUSHTXTBTN, RESIZE_TB, 10, 0, 143, 213, 224, STR_NEWGRF_CHECK_AVAILABLE, STR_NULL }, // DNGRFS_CHECK_AVILABLE
{ WWT_PUSHTXTBTN, RESIZE_RTB, 10, 144, 287, 213, 224, STR_NEWGRF_DOWNLOAD_AVAILABLE, STR_NULL }, // DNGRFS_DOWNLOAD_AVAILABLE
{ WWT_RESIZEBOX, RESIZE_LRTB, 10, 288, 299, 213, 224, 0x0, STR_RESIZE_BUTTON }, // DNGRFS_RESIZE
{ WIDGETS_END },
};
/* Window definition of the manage newgrfs window */
static const WindowDesc _newgrf_download_desc = {
WDP_CENTER, WDP_CENTER, 300, 225, 300, 225,
WC_GAME_OPTIONS, WC_NONE,
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE,
_newgrf_download_widgets,
};
/** Setup the NewGRF download gui
* @param config pointer to a linked-list of grfconfig's needed */
void ShowNewGRFDownload(struct NetworkGameInfo *info)
{
// filter out those that we don't need to download
GRFConfig *head = NULL, **item = &head;
const GRFConfig *src;
for (src = info->grfconfig; src != NULL; src = src->next) {
if (src->status == GCS_NOT_FOUND || HasBit(src->flags, GCF_COMPATIBLE)) {
// copy-past from newgrf_config.cpp CopyGRFConfigList
GRFConfig *c = CallocT<GRFConfig>(1);
*c = *src;
c->next = NULL;
if (src->filename != NULL) c->filename = strdup(src->filename);
if (src->name != NULL) c->name = strdup(src->name);
if (src->info != NULL) c->info = strdup(src->info);
if (src->error != NULL) {
c->error = CallocT<GRFError>(1);
memcpy(c->error, src->error, sizeof(GRFError));
if (src->error->data != NULL) c->error->data = strdup(src->error->data);
if (src->error->custom_message != NULL) c->error->custom_message = strdup(src->error->custom_message);
}
*item = c;
item = &c->next;
}
}
DeleteWindowByClass(WC_GAME_OPTIONS);
new NewGRFDownloadWindow(&_newgrf_download_desc, info, head);
}