mirror of
https://github.com/LIJI32/SameBoy.git
synced 2026-10-12 02:06:38 +00:00
161 lines
4.6 KiB
C
161 lines
4.6 KiB
C
#include "thumbnail.h"
|
|
|
|
#include <gio/gio.h>
|
|
#include <glib.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "emulate.h"
|
|
#include "main.h"
|
|
#include "tasks.h"
|
|
|
|
#define THUMBNAILING_ERROR_DOMAIN (g_quark_from_static_string("thumbnailing"))
|
|
|
|
/* --- */
|
|
|
|
enum CartridgeType {
|
|
CART_DMG_ONLY,
|
|
CART_DUAL,
|
|
CART_CGB_ONLY,
|
|
};
|
|
|
|
void load_cartridge_images(void) {
|
|
// TODO
|
|
}
|
|
|
|
/* --- */
|
|
|
|
struct TaskData {
|
|
char *contents;
|
|
size_t length;
|
|
enum FileKind kind;
|
|
};
|
|
|
|
static void destroy_task_data(void *data)
|
|
{
|
|
struct TaskData *task_data = data;
|
|
|
|
g_free(task_data->contents);
|
|
g_slice_free(struct TaskData, task_data);
|
|
}
|
|
|
|
/* --- */
|
|
|
|
static void generate_thumbnail(GTask *task, void *source_object, void *data,
|
|
GCancellable *cancellable)
|
|
{
|
|
struct TaskData *task_data = data;
|
|
|
|
uint32_t screen[160 * 144];
|
|
unsigned cgb_flag = emulate(task_data->kind, (unsigned char const *)task_data->contents,
|
|
task_data->length, screen);
|
|
|
|
// Generate the thumbnail from `screen` and `cgb_flag`.
|
|
enum CartridgeType type;
|
|
switch (cgb_flag) {
|
|
case 0xC0:
|
|
type = CART_CGB_ONLY;
|
|
break;
|
|
case 0x80:
|
|
type = CART_DUAL;
|
|
break;
|
|
default:
|
|
type = CART_DMG_ONLY;
|
|
break;
|
|
}
|
|
(void)type;
|
|
|
|
g_task_return_boolean(task, TRUE);
|
|
g_object_unref(task);
|
|
}
|
|
|
|
// Callback when an async file operation completes.
|
|
static void on_file_ready(GObject *source_object, GAsyncResult *res, void *user_data)
|
|
{
|
|
GFile *file = G_FILE(source_object);
|
|
GTask *task = user_data;
|
|
char const *uri = g_task_get_name(task);
|
|
g_debug("File \"%s\" is done being read", uri);
|
|
struct TaskData *task_data = g_task_get_task_data(task);
|
|
|
|
GError *error = NULL;
|
|
g_file_load_contents_finish(file, res, &task_data->contents, &task_data->length, NULL, &error);
|
|
g_object_unref(file);
|
|
|
|
if (error) {
|
|
g_task_return_new_error(task, THUMBNAILING_ERROR_DOMAIN, ERROR_UNKNOWN_SCHEME_OR_MIME,
|
|
"Failed to load URI \"%s\": %s", uri, error->message);
|
|
g_object_unref(task);
|
|
return;
|
|
}
|
|
|
|
if (g_task_return_error_if_cancelled(task)) {
|
|
g_object_unref(task);
|
|
return;
|
|
}
|
|
|
|
// TODO: cap the max number of active threads.
|
|
g_task_run_in_thread(task, generate_thumbnail);
|
|
}
|
|
|
|
/* --- */
|
|
|
|
static void on_thumbnailing_end(GObject *source_object, GAsyncResult *res, void *user_data)
|
|
{
|
|
// TODO: start a new thread if some task is pending.
|
|
|
|
g_assert_null(source_object); // The object that was passed to `g_task_new`.
|
|
GTask *task = G_TASK(res);
|
|
g_info("Ending thumbnailing for \"%s\"", g_task_get_name(task));
|
|
unsigned handle = GPOINTER_TO_UINT(user_data);
|
|
char const *uri = g_task_get_name(task);
|
|
|
|
GError *error = NULL;
|
|
if (g_task_propagate_boolean(task, &error)) {
|
|
g_signal_emit_by_name(thumbnailer_interface, "ready", handle, uri);
|
|
}
|
|
else if (!g_cancellable_is_cancelled(g_task_get_cancellable(task))) {
|
|
// If the task was cancelled, do not emit an error response.
|
|
g_signal_emit_by_name(thumbnailer_interface, "error", handle, uri, error->code,
|
|
error->message);
|
|
}
|
|
g_signal_emit_by_name(thumbnailer_interface, "finished", handle);
|
|
|
|
finished_task(handle);
|
|
}
|
|
|
|
void start_thumbnailing(unsigned handle, GCancellable *cancellable, gboolean is_urgent,
|
|
char const *uri, char const *mime_type)
|
|
{
|
|
g_signal_emit_by_name(thumbnailer_interface, "started", handle);
|
|
|
|
GTask *task = g_task_new(NULL, cancellable, on_thumbnailing_end, GUINT_TO_POINTER(handle));
|
|
g_task_set_priority(task, is_urgent ? G_PRIORITY_HIGH : G_PRIORITY_DEFAULT);
|
|
g_task_set_name(task, uri);
|
|
|
|
enum FileKind kind;
|
|
if (g_strcmp0(mime_type, "application/x-gameboy-color-rom") == 0) {
|
|
kind = KIND_GBC;
|
|
}
|
|
else if (g_strcmp0(mime_type, "application/x-gameboy-rom") == 0) {
|
|
kind = KIND_GB;
|
|
}
|
|
else if (g_strcmp0(mime_type, "application/x-gameboy-isx") == 0) {
|
|
kind = KIND_ISX;
|
|
}
|
|
else {
|
|
g_task_return_new_error(task, THUMBNAILING_ERROR_DOMAIN, ERROR_UNKNOWN_SCHEME_OR_MIME,
|
|
"Unsupported MIME type %s", mime_type);
|
|
g_object_unref(task);
|
|
return;
|
|
}
|
|
|
|
struct TaskData *task_data = g_slice_new(struct TaskData);
|
|
task_data->contents = NULL;
|
|
task_data->kind = kind;
|
|
g_task_set_task_data(task, task_data, destroy_task_data);
|
|
|
|
GFile *file = g_file_new_for_uri(uri);
|
|
g_file_load_contents_async(file, cancellable, on_file_ready, task);
|
|
}
|