1
0
mirror of https://git.code.sf.net/p/fuse-emulator/fuse synced 2026-01-27 01:41:34 +03:00
Files
fuse/ui/widget/pokemem.c
2016-10-24 11:45:18 +11:00

523 lines
13 KiB
C

/* pokemem.c: The poke memory widget
Copyright (c) 2011 Philip Kendall, Sergio Baldoví
Copyright (c) 2015 Adrien Destugues
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.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Author contact information:
E-mail: philip-fuse@shadowmagic.org.uk
*/
#include <config.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "fuse.h"
#include "menu.h"
#include "pokefinder/pokemem.h"
#include "ui/ui.h"
#include "widget.h"
#include "widget_internals.h"
typedef struct entry_t {
int checked;
trainer_t *trainer;
} entry_t;
const char *pokemem_title = "Poke memory";
const unsigned int page_size = 16;
GArray *store = NULL;
int selected = -1;
int highlight_line = 0;
unsigned int menu_left_edge_x;
unsigned int menu_width;
unsigned int pokemem_count = 0;
unsigned int top_index = 0;
unsigned int widget_pokemem_calculate_width( void );
void widget_pokemem_store_new( void );
void widget_pokemem_store_add( gpointer data, gpointer user_data );
void widget_pokemem_print_list( unsigned int left_edge, unsigned int width );
int widget_pokemem_print_trainer( unsigned int left_edge, unsigned int width,
int number, int disabled, int checked,
const char *string );
void widget_pokemem_update_line( unsigned int left_edge, unsigned int width,
int index );
int widget_pokemem_trainer_click( int index );
void widget_pokemem_ask_value( trainer_t *trainer );
int widget_pokemem_add_custom_poke( void );
void widget_pokemem_apply_pokes( void );
void
ui_pokemem_selector( const char *filename )
{
fuse_emulation_pause();
pokemem_read_from_file( filename );
menu_machine_pokememory( 0 );
fuse_emulation_unpause();
}
int widget_pokemem_finish( widget_finish_state finished )
{
if( finished == WIDGET_FINISHED_OK ) {
widget_pokemem_apply_pokes();
}
if( store ) {
g_array_free( store, TRUE );
store = NULL;
}
pokemem_count = 0;
return 0;
}
int
widget_pokemem_draw( void *data GCC_UNUSED )
{
if( !store ) {
pokemem_autoload_pokfile();
widget_pokemem_store_new();
}
menu_width = widget_pokemem_calculate_width();
menu_left_edge_x = DISPLAY_WIDTH_COLS / 2 - menu_width / 2;
widget_dialog_with_border( menu_left_edge_x, 2, menu_width, page_size + 4 );
widget_printstring( menu_left_edge_x * 8 + 2, 16, WIDGET_COLOUR_TITLE,
pokemem_title );
widget_pokemem_print_list( menu_left_edge_x, menu_width );
widget_printstring( menu_left_edge_x * 8 + 8, ( page_size + 4 ) * 8,
WIDGET_COLOUR_FOREGROUND, "\x0A" "A\x01" "dd" );
widget_display_lines( 2, page_size + 4 );
return 0;
}
unsigned int
widget_pokemem_calculate_width( void )
{
unsigned int i, width, max_width, text_width;
entry_t *entry;
trainer_t *trainer;
if( !store ) return 25;
max_width = 0;
for( i = 0; i < pokemem_count; i++ ) {
entry = &g_array_index( store, entry_t, i );
trainer = entry->trainer;
text_width = widget_stringwidth( trainer->name ) + 3 * 8;
if( text_width > max_width ) max_width = text_width;
}
width = ( max_width + 16 ) / 8;
if( width < 25 )
width = 25;
else if( width > 32 )
width = 32;
return width;
}
void
widget_pokemem_print_list( unsigned int left_edge, unsigned int width )
{
char buf[32];
unsigned int i, page_limit;
entry_t *entry;
trainer_t *trainer;
if( store && pokemem_count ) {
page_limit = top_index + page_size;
for( i = top_index; i < pokemem_count && i < page_limit; i++ ) {
entry = &g_array_index( store, entry_t, i );
trainer = entry->trainer;
snprintf( buf, sizeof( buf ), "%s", trainer->name );
widget_pokemem_print_trainer( left_edge, width, i - top_index,
trainer->disabled, entry->checked, buf );
}
if( top_index ) widget_up_arrow( left_edge, 3, WIDGET_COLOUR_FOREGROUND );
if( i < pokemem_count )
widget_down_arrow( left_edge, page_size + 2, WIDGET_COLOUR_FOREGROUND );
}
widget_display_lines( 3, page_size );
}
int
widget_pokemem_print_trainer( unsigned int left_edge, unsigned int width,
int number, int disabled, int checked,
const char *string )
{
char buffer[128];
size_t l, w;
int colour = WIDGET_COLOUR_BACKGROUND;
int x;
int y;
if( number == highlight_line ) colour = WIDGET_COLOUR_HIGHLIGHT;
widget_rectangle( left_edge * 8 + 1, number * 8 + 24, width * 8 - 2, 1 * 8,
colour );
snprintf( buffer, sizeof( buffer ), "%s", string );
l = strlen( buffer );
if( l >= sizeof( buffer ) )
l = sizeof( buffer ) - 1;
while( ( w = widget_substringwidth( string, l ) ) >= ( left_edge+width-2 )*8 )
--l;
buffer[l] = '\0';
w = widget_printstring( left_edge * 8 + 9, number * 8 + 24,
WIDGET_COLOUR_FOREGROUND, buffer ) - 1;
while( ( w += 3 ) < ( left_edge + width - 1 ) * 8 - 2 )
widget_putpixel( w, number * 8 + 31, 0 );
/* print check */
x = ( left_edge + width - 2 ) * 8 - 2;
y = number * 8 + 24;
widget_rectangle( x, y, 8, 8, colour );
widget_print_checkbox( x, y,
( disabled )? WIDGET_COLOUR_FOREGROUND : colour,
checked );
widget_display_rasters( y, 8 );
return 0;
}
void
widget_pokemem_update_line( unsigned int left_edge, unsigned int width,
int index )
{
char buf[32];
entry_t *entry;
trainer_t *trainer;
if( !store ) return;
entry = &g_array_index( store, entry_t, index );
trainer = entry->trainer;
snprintf( buf, sizeof( buf ), "%s", trainer->name );
widget_pokemem_print_trainer( left_edge, width, index - top_index,
trainer->disabled, entry->checked, buf );
/* First row in page */
if( top_index && index == top_index )
widget_up_arrow( left_edge, 3, WIDGET_COLOUR_FOREGROUND );
/* Last row in page */
if( top_index + page_size < pokemem_count &&
( index - top_index == page_size - 1 ) )
widget_down_arrow( left_edge, page_size + 2, WIDGET_COLOUR_FOREGROUND );
}
void
widget_pokemem_keyhandler( input_key key )
{
int new_selected;
new_selected = selected;
switch ( key ) {
case INPUT_KEY_Return: /* Do pokes */
case INPUT_KEY_KP_Enter:
case INPUT_JOYSTICK_FIRE_1:
widget_end_all( WIDGET_FINISHED_OK );
break;
case INPUT_KEY_Escape: /* Close widget */
case INPUT_JOYSTICK_FIRE_2:
widget_end_widget( WIDGET_FINISHED_CANCEL );
return;
case INPUT_KEY_a: /* Add poke */
if( !widget_pokemem_add_custom_poke() ) new_selected = pokemem_count - 1;
break;
case INPUT_KEY_Home:
new_selected = 0;
break;
case INPUT_KEY_End:
new_selected = pokemem_count - 1;
break;
case INPUT_KEY_Page_Up:
new_selected = ( selected > page_size )? selected - page_size : 0;
break;
case INPUT_KEY_Page_Down:
new_selected = selected + page_size;
if( new_selected >= pokemem_count ) new_selected = pokemem_count - 1;
break;
case INPUT_KEY_Up:
case INPUT_KEY_7:
case INPUT_JOYSTICK_UP:
if( selected ) new_selected = selected - 1;
break;
case INPUT_KEY_Down:
case INPUT_KEY_6:
case INPUT_JOYSTICK_DOWN:
if( selected + 1 < pokemem_count ) new_selected = selected + 1;
break;
case INPUT_KEY_space:
case INPUT_KEY_8:
case INPUT_JOYSTICK_RIGHT:
if( !widget_pokemem_trainer_click( selected ) )
widget_pokemem_update_line( menu_left_edge_x, menu_width, selected );
return;
default:;
}
if( store && new_selected != selected ) {
if( new_selected < top_index ) {
top_index = new_selected;
highlight_line = new_selected - top_index;
widget_pokemem_print_list( menu_left_edge_x, menu_width );
}
else if( new_selected >= top_index + page_size ) {
top_index = new_selected - page_size + 1;
highlight_line = new_selected - top_index;
widget_pokemem_print_list( menu_left_edge_x, menu_width );
} else {
/* Otherwise, print the current trainer uninverted and the
new current trainer inverted */
highlight_line = new_selected - top_index;
if( selected >= 0 )
widget_pokemem_update_line( menu_left_edge_x, menu_width, selected );
widget_pokemem_update_line( menu_left_edge_x, menu_width, new_selected );
widget_display_lines( 3, page_size );
}
selected = new_selected;
}
}
void
widget_pokemem_store_new( void )
{
if( !trainer_list ) return;
store = g_array_new( FALSE, FALSE, sizeof( entry_t ) );
if( store ) {
g_slist_foreach( trainer_list, widget_pokemem_store_add, NULL );
pokemem_count = store->len;
}
/* Adjust default selection to current data */
if( pokemem_count == 0 ) {
selected = -1;
top_index = 0;
highlight_line = 0;
}
else if( selected > pokemem_count ) {
selected = 0;
top_index = 0;
highlight_line = 0;
}
}
void
widget_pokemem_store_add( gpointer data, gpointer user_data GCC_UNUSED )
{
trainer_t *trainer = data;
entry_t entry;
if( !trainer ) return;
/* Append a new row and fill data */
entry.checked = trainer->active;
entry.trainer = trainer;
g_array_append_vals( store, &entry, 1 );
}
void
widget_pokemem_apply_pokes( void )
{
entry_t *entry;
trainer_t *trainer;
unsigned int i;
if( !store ) return;
for( i = 0; i < store->len; i++ ) {
entry = &g_array_index( store, entry_t, i );
trainer = entry->trainer;
if( entry->checked ) {
pokemem_trainer_activate( trainer );
} else {
pokemem_trainer_deactivate( trainer );
}
}
}
int
widget_pokemem_trainer_click( int index )
{
entry_t *entry;
trainer_t *trainer;
if( !store ) return 1;
/* Disable incomplete trainers or active without restore value */
entry = &g_array_index( store, entry_t, index );
trainer = entry->trainer;
if( trainer->disabled ) return 1;
/* Toggle current selection */
entry->checked = !entry->checked;
widget_pokemem_update_line( menu_left_edge_x, menu_width, selected );
/* Request user for custom value */
if( entry->checked && trainer->ask_value ) {
widget_pokemem_ask_value( trainer );
}
return 0;
}
void
widget_pokemem_ask_value( trainer_t *trainer )
{
int value;
widget_text_t text_data;
text_data.title = "Enter trainer value";
text_data.allow = WIDGET_INPUT_DIGIT;
text_data.max_length = 3;
snprintf( text_data.text, sizeof( text_data.text ), "%d", trainer->value );
widget_do_text( &text_data );
if( widget_text_text ) {
value = atoi( widget_text_text );
trainer->value = ( value > 255 )? 0 : value;
}
}
int
widget_pokemem_add_custom_poke( void )
{
long b, a, v;
trainer_t *trainer;
entry_t entry;
widget_text_t text_data;
char *endptr;
/* Bank */
memset( &text_data, 0, sizeof( widget_text_t ) );
text_data.title = "Enter bank (optional)";
text_data.allow = WIDGET_INPUT_DIGIT;
text_data.max_length = 1;
if( widget_do_text( &text_data ) ) return 1;
if( !widget_text_text ) return 1;
errno = 0;
b = strtol( widget_text_text, &endptr, 10 );
if( errno || b < 0 || b > 8 ) {
ui_error( UI_ERROR_ERROR, "Invalid bank: use an integer from 0 to 8" );
return 1;
}
if( endptr == widget_text_text ) b = 8; /* ignore bank by default */
/* Address */
text_data.title = "Enter address / offset";
text_data.max_length = 5;
if( widget_do_text( &text_data ) ) return 1;
if( !widget_text_text ) return 1;
errno = 0;
a = strtol( widget_text_text, &endptr, 10 );
if( errno || a < 0 || a > 65535 || endptr == widget_text_text ) {
ui_error( UI_ERROR_ERROR,
"Invalid address: use an integer from 0 to 65535" );
return 1;
}
if( b == 8 && a < 16384 ) {
ui_error( UI_ERROR_ERROR,
"Invalid address: use an integer from 16384 to 65535" );
return 1;
}
/* Value */
text_data.title = "Enter value";
text_data.max_length = 3;
if( widget_do_text( &text_data ) ) return 1;
if( !widget_text_text ) return 1;
errno = 0;
v = strtol( widget_text_text, &endptr, 10 );
if( errno || v < 0 || v > 256 || endptr == widget_text_text ) {
ui_error( UI_ERROR_ERROR, "Invalid value: use an integer from 0 to 256" );
return 1;
}
trainer = pokemem_trainer_list_add( b, a, v );
if( !trainer ) {
ui_error( UI_ERROR_ERROR, "Cannot add trainer" );
return 1;
}
/* Append a new row and fill data */
entry.trainer = trainer;
entry.checked = trainer->active;
if( !trainer->active && !trainer->disabled && !trainer->ask_value ) {
entry.checked = 1;
}
if( !store ) {
store = g_array_new( FALSE, FALSE, sizeof( entry_t ) );
if( !store ) return 1;
}
g_array_append_vals( store, &entry, 1 );
pokemem_count = store->len;
return 0;
}