forked from Lephenixnoir/hex-editor
212 lines
5.8 KiB
C
212 lines
5.8 KiB
C
#include <gint/display.h>
|
|
#include <gint/keyboard.h>
|
|
#include <gint/exc.h>
|
|
|
|
#include <justui/jscene.h>
|
|
#include <justui/jlabel.h>
|
|
#include <justui/jfkeys.h>
|
|
#include <justui/jinput.h>
|
|
#include <justui/jpainted.h>
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
struct view {
|
|
uint32_t base;
|
|
bool ascii;
|
|
int lines;
|
|
};
|
|
|
|
/* Code of exception that occurs during a memory access */
|
|
static uint32_t exception = 0;
|
|
/* Exception-catching function */
|
|
static int catch_exc(uint32_t code)
|
|
{
|
|
if(code == 0x040 || code == 0x0e0) {
|
|
exception = code;
|
|
gint_exc_skip(1);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int line(uint8_t *mem, char *header, char *bytes, char *ascii, int n)
|
|
{
|
|
/* First do a naive access to the first byte, and record possible
|
|
exceptions - TLB miss read and CPU read error.
|
|
I use a volatile asm statement so that the read can't be optimized
|
|
away or moved by the compiler. */
|
|
exception = 0;
|
|
gint_exc_catch(catch_exc);
|
|
uint8_t z;
|
|
__asm__ volatile("mov.l %1, %0" : "=r"(z) : "m"(*mem));
|
|
gint_exc_catch(NULL);
|
|
|
|
sprintf(header, "%08X:", (uint32_t)mem);
|
|
|
|
if(exception == 0x040) {
|
|
sprintf(bytes, "TLB error");
|
|
memset(ascii, 0, n);
|
|
return 1;
|
|
}
|
|
if(exception == 0x0e0) {
|
|
sprintf(bytes, "Read error");
|
|
memset(ascii, 0, n);
|
|
return 1;
|
|
}
|
|
|
|
/* Read single bytes when possible, but longwords when in SPU memory */
|
|
for(int k = 0; k < n; k++) {
|
|
uint32_t addr = (uint32_t)mem;
|
|
int c = 0x11;
|
|
|
|
/* XRAM, YRAM, PRAM */
|
|
if((addr & 0xffc00000) == 0xfe000000) {
|
|
uint32_t l = *(uint32_t *)(addr & ~3);
|
|
c = (l << (addr & 3) * 8) >> 24;
|
|
}
|
|
else c = mem[k];
|
|
|
|
ascii[k] = (c >= 0x20 && c < 0x7f) ? c : '.';
|
|
}
|
|
ascii[n] = 0;
|
|
|
|
for(int k = 0; 2 * k < n; k++)
|
|
sprintf(bytes + 5 * k, "%02X%02X ", mem[2*k], mem[2*k+1]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void paint_mem(int x, int y, struct view *v)
|
|
{
|
|
char header[12], bytes[48], ascii[24];
|
|
uint32_t addr = v->base;
|
|
uint8_t *mem = (void *)addr;
|
|
|
|
for(int i = 0; i < v->lines; i++, mem += 8, addr += 8) {
|
|
int status = line(mem, header, bytes, ascii, 8);
|
|
|
|
dtext(x, y + 12*i, C_BLACK, header);
|
|
dtext(x + 85, y + 12*i, status ? C_RED : C_BLACK, bytes);
|
|
|
|
for(int k = 7; k >= 0; k--) {
|
|
ascii[k+1] = 0;
|
|
dtext(x + 250 + 9*k, y + 12*i, C_BLACK, ascii+k);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void update_status_bar(jlabel *status, struct view *v)
|
|
{
|
|
jlabel_asprintf(status, "%X/%X", v->base, 8*v->lines);
|
|
}
|
|
|
|
void hex_view(void)
|
|
{
|
|
struct view v = { .base = 0x88000000, .ascii = false, .lines = 13 };
|
|
|
|
jscene *scene = jscene_create_fullscreen(NULL);
|
|
jlabel *title = jlabel_create("Hex Editor", scene);
|
|
jwidget *stack = jwidget_create(scene);
|
|
jfkeys *fkeys = jfkeys_create("@JUMP;;#ROM;#RAM;#ILRAM;#ADDIN", scene);
|
|
|
|
if(!scene || !title || !stack || !fkeys) {
|
|
jwidget_destroy(scene);
|
|
return;
|
|
}
|
|
|
|
jwidget_set_background(title, C_BLACK);
|
|
jlabel_set_text_color(title, C_WHITE);
|
|
jwidget_set_stretch(title, 1, 0, false);
|
|
jwidget_set_padding(title, 3, 6, 3, 6);
|
|
|
|
jlayout_set_vbox(scene)->spacing = 3;
|
|
jlayout_set_stack(stack);
|
|
jwidget_set_padding(stack, 0, 6, 0, 6);
|
|
jwidget_set_stretch(stack, 1, 1, false);
|
|
|
|
// Main tab //
|
|
|
|
jwidget *tab = jwidget_create(NULL);
|
|
jpainted *mem = jpainted_create(paint_mem, &v, 321, 12*v.lines-3, tab);
|
|
jlabel *status = jlabel_create("", tab);
|
|
jinput *input = jinput_create("Go to: ", 12, tab);
|
|
|
|
jwidget_set_margin(mem, 4, 0, 4, 0);
|
|
jwidget_set_stretch(mem, 1, 0, false);
|
|
|
|
jwidget_set_stretch(status, 1, 0, false);
|
|
|
|
jwidget_set_margin(input, 0, 0, 0, 4);
|
|
jwidget_set_stretch(input, 1, 0, false);
|
|
jwidget_set_visible(input, false);
|
|
|
|
jlayout_set_vbox(tab)->spacing = 4;
|
|
jwidget_add_child(stack, tab);
|
|
jwidget_set_stretch(tab, 1, 1, false);
|
|
|
|
update_status_bar(status, &v);
|
|
|
|
// Event handling //
|
|
|
|
int key = 0;
|
|
while(key != KEY_EXIT)
|
|
{
|
|
bool input_focus = (jscene_focused_widget(scene) == input);
|
|
jevent e = jscene_run(scene);
|
|
|
|
if(e.type == JSCENE_PAINT) {
|
|
dclear(C_WHITE);
|
|
jscene_render(scene);
|
|
dupdate();
|
|
}
|
|
if(e.type == JINPUT_VALIDATED) {
|
|
/* Parse string into hexa */
|
|
uint32_t target = 0;
|
|
char const *str = jinput_value(input);
|
|
|
|
for(int k = 0; k < str[k]; k++)
|
|
{
|
|
target <<= 4;
|
|
if(str[k] <= '9') target += (str[k] - '0');
|
|
else target += ((str[k]|0x20) - 'a' + 10);
|
|
}
|
|
v.base = target & ~7;
|
|
}
|
|
if(e.type == JINPUT_VALIDATED || e.type == JINPUT_CANCELED) {
|
|
jwidget_set_visible(input, false);
|
|
jwidget_set_visible(fkeys, true);
|
|
jscene_set_focused_widget(scene, NULL);
|
|
}
|
|
|
|
if(e.type != JSCENE_KEY || e.key.type == KEYEV_UP) continue;
|
|
key = e.key.key;
|
|
|
|
int move_speed = (e.key.shift ? 8 : 1);
|
|
if(key == KEY_UP) v.base -= move_speed * 8 * v.lines;
|
|
if(key == KEY_DOWN) v.base += move_speed * 8 * v.lines;
|
|
|
|
if(key == KEY_F1 && !input_focus) {
|
|
jinput_clear(input);
|
|
jwidget_set_visible(input, true);
|
|
jwidget_set_visible(fkeys, false);
|
|
jscene_set_focused_widget(scene, input);
|
|
}
|
|
|
|
if(key == KEY_F3 && !input_focus) v.base = 0x80000000;
|
|
if(key == KEY_F4 && !input_focus) v.base = 0x88000000;
|
|
if(key == KEY_F5 && !input_focus) v.base = 0xe5200000;
|
|
if(key == KEY_F6 && !input_focus) v.base = 0x00300000;
|
|
mem->widget.update = 1;
|
|
|
|
update_status_bar(status, &v);
|
|
}
|
|
jwidget_destroy(scene);
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
hex_view();
|
|
return 1;
|
|
}
|