rust as intended

This commit is contained in:
Travis Burtrum 2021-10-28 23:24:57 -04:00
parent 3359073498
commit 450335c79a
7 changed files with 449 additions and 80 deletions

1
.gitignore vendored
View File

@ -18,3 +18,4 @@
*properties.json
/.vs
/.vscode
*/.idea/

View File

@ -26,9 +26,17 @@ RUST_DEPS := $(wildcard $(RUST_DIR)/src/*) $(wildcard $(RUST_DIR)/Cargo.*)
RUST_BIN_DEPS := $(RUST_DEPS) $(RUST_DIR)/mips-nintendo64-none.json
RUST_H_DEPS := $(RUST_DEPS) $(RUST_DIR)/cbindgen.toml
LINK_FLAGS = -O1 -L$(ROOTDIR)/lib -L$(ROOTDIR)/mips64-elf/lib -ldragon -lmad -lyaml -lc -lm -ldragonsys -lnosys -L$(RUST_FULL_TARGET_DIR) -laltra64 $(LIBS) -Tn64ld.x
PROG_NAME = $(BINDIR)/OS64P
CFLAGS = -std=gnu99 -march=vr4300 -mtune=vr4300 -O1 -I$(INCDIR) -I$(ROOTDIR)/include -I$(ROOTDIR)/mips64-elf/include -I$(RUST_FULL_TARGET_DIR) -lpthread -lrt -D_REENTRANT -DUSE_TRUETYPE $(SET_DEBUG)
ifdef USE_YAML
CFLAGS += -DUSE_YAML
LIBS += -lyaml
else
LIBS += -laltra64
endif
LINK_FLAGS = -O1 -L$(ROOTDIR)/lib -L$(ROOTDIR)/mips64-elf/lib -L$(RUST_FULL_TARGET_DIR) -ldragon -lmad $(LIBS) -lc -lm -ldragonsys -lnosys -Tn64ld.x
PROG_NAME = $(BINDIR)/OS64P
ASFLAGS = -mtune=vr4300 -march=vr4300
CC = $(GCCN64PREFIX)gcc
AS = $(GCCN64PREFIX)as

View File

@ -2,6 +2,13 @@
name = "altra64"
version = "0.1.0"
authors = ["moparisthebest <admin@moparisthebest.com>"]
edition = "2018"
[dependencies]
#libc = {version = "0.2", default-features = false}
#no-std-compat = { version = "0.4.1", features = [ "alloc" ] }
#libdragon-bindings = { git = "https://github.com/DagothBob/libdragon-bindings" }
#cstr_core = {version = "0.2", default-features = false}
# the profile used for `cargo build`
[profile.dev]

View File

@ -15,3 +15,5 @@ include_guard = "ALTRA64_H"
autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
include_version = false
[export]
exclude = ["strtoul"]

View File

@ -1,5 +1,241 @@
#![no_std]
use core::slice;
use core::iter::Iterator;
const WHITESPACE: &[u8] = b" \t\n\r";
pub trait SliceSubsequence<T> {
fn trim_start(&self, needle: &[T]) -> &[T];
fn trim_end(&self, needle: &[T]) -> &[T];
fn first_index_of(&self, needle: &[T]) -> Option<usize>;
//fn extract_between(&self, before: &[T], after: &[T]) -> Option<&[T]>;
fn lines<'a>(&'a self, needle: &'a [T]) -> LineIterator<'a, T>;
fn trim(&self, needle: &[T]) -> &[T];
fn contains_seq(&self, needle: &[T]) -> bool {
self.first_index_of(needle).is_some()
}
}
pub struct LineIterator<'a, T> {
inner: &'a [T],
needle: &'a [T],
pos: usize,
}
impl<'a, T: PartialEq> Iterator for LineIterator<'a, T> {
type Item = &'a [T];
fn next(&mut self) -> Option<Self::Item> {
if self.pos == self.inner.len() {
return None;
}
let search = &self.inner[self.pos..];
match search.first_index_of(self.needle) {
None => {
let pos = self.pos;
self.pos = self.inner.len();
Some(&self.inner[pos..])
}
Some(idx) => {
self.pos += idx + self.needle.len();
Some(&search[..idx])
}
}
}
}
fn last_index_of<T: PartialEq>(s: &[T], needle: &[T]) -> usize {
let mut len = 0;
for i in s {
if needle.contains(i) {
len += 1;
} else {
break;
}
}
len
}
fn last_index_of_rev<T: PartialEq>(s: &[T], needle: &[T]) -> usize {
let mut len = s.len();
for i in s.iter().rev() {
if needle.contains(i) {
len -= 1;
} else {
break;
}
}
len
}
impl<T: PartialEq> SliceSubsequence<T> for &[T] {
fn trim_start(&self, needle: &[T]) -> &[T] {
&self[last_index_of(self, needle)..]
}
fn trim_end(&self, needle: &[T]) -> &[T] {
&self[..last_index_of_rev(self, needle)]
}
fn first_index_of(&self, needle: &[T]) -> Option<usize> {
if self.len() >= needle.len() {
for i in 0..self.len() - needle.len() + 1 {
if self[i..i + needle.len()] == needle[..] {
return Some(i);
}
}
}
None
}
fn lines<'a>(&'a self, needle: &'a [T]) -> LineIterator<'a, T> {
LineIterator {
inner: self,
needle,
pos: 0,
}
}
fn trim(&self, needle: &[T]) -> &[T] {
let start = last_index_of(self, needle);
let end = last_index_of_rev(self, needle);
if start >= end {
// empty
&self[0..0]
} else {
&self[start..end]
}
}
}
#[no_mangle]
pub extern "C" fn parse_cheats_ffi(
cheat_file: *mut u8, cheat_file_len: usize,
boot_cheats: *mut u32, boot_cheats_len: usize,
in_game_cheats: *mut u32, in_game_cheats_len: usize,
) -> u8 {
let cheat_file = unsafe { slice::from_raw_parts(cheat_file, cheat_file_len) };
let boot_cheats = unsafe { slice::from_raw_parts_mut(boot_cheats, boot_cheats_len) };
let in_game_cheats = unsafe { slice::from_raw_parts_mut(in_game_cheats, in_game_cheats_len) };
parse_cheats(cheat_file, boot_cheats, in_game_cheats)
}
const SUCCESS: u8 = 0;
const INVALID_CODE_LINE: u8 = 1;
const INVALID_LINE: u8 = 2;
pub fn parse_cheats(cheat_file: &[u8], boot_cheats: &mut [u32], in_game_cheats: &mut [u32]) -> u8 {
let mut repeater = false;
let mut boot_cheats_idx = 0;
let mut in_game_cheats_idx = 0;
for line in cheat_file.lines(b"\n") {
let line = line.trim(WHITESPACE);
if line.is_empty() || line.starts_with(b"#") || line == b"---" {
continue; // empty or comment or whatever the starting thing is
} else if line.ends_with(b":") {
repeater = false;
} else if line.starts_with(b"- ") {
// we found the start of a code
let line = line.trim_start(b"- ");
let line = line.trim(WHITESPACE);
let mut line = line.lines(b" ");
match (line.next(), line.next(), line.next()) {
(Some(address), Some(value), None) => {
// proper line
let address = hex_to_u32(address.trim(WHITESPACE));
let value = hex_to_u32(value.trim(WHITESPACE));
//println!("address: {:X}, value: {:X}", address, value);
//println!("dec address: {}, value: {}", address, value);
// Do not check code types within "repeater data"
if repeater {
repeater = false;
in_game_cheats[in_game_cheats_idx] = address;
in_game_cheats_idx += 1;
in_game_cheats[in_game_cheats_idx] = value;
in_game_cheats_idx += 1;
continue;
}
//println!("address >> 24: {:X}", address >> 24);
// Determine destination cheat_list for the code type
match address >> 24
{
// Uncessary code types
0x20 | // Clear code list
0xCC | // Exception Handler Selection
0xDE => // Entry Point
continue,
// Boot-time cheats
0xEE | // Disable Expansion Pak
0xF0 | // 8-bit Boot-Time Write
0xF1 | // 16-bit Boot-Time Write
0xFF => { // Cheat Engine Location
boot_cheats[boot_cheats_idx] = address;
boot_cheats_idx += 1;
boot_cheats[boot_cheats_idx] = value;
boot_cheats_idx += 1;
}
// In-game cheats
0x50 => { // Repeater/Patch
// Validate repeater count
if (address & 0x0000FF00) == 0 {
repeater = true;
in_game_cheats[in_game_cheats_idx] = address;
in_game_cheats_idx += 1;
in_game_cheats[in_game_cheats_idx] = value;
in_game_cheats_idx += 1;
}
}
// todo: was fallthrough from default in C, does that even work?
0xD0 | // 8-bit Equal-To Conditional
0xD1 | // 16-bit Equal-To Conditional
0xD2 | // 8-bit Not-Equal-To Conditional
0xD3 => { // 16-bit Not-Equal-To Conditional
// Validate 16-bit codes
if (address & 0x01000001) == 0x01000001 {
continue; // todo: or error
}
in_game_cheats[in_game_cheats_idx] = address;
in_game_cheats_idx += 1;
in_game_cheats[in_game_cheats_idx] = value;
in_game_cheats_idx += 1;
}
// Everything else
_ => {
if address != 0
{
// TODO: Support special code types! :)
}
// Validate 16-bit codes
if (address & 0x01000001) == 0x01000001 {
continue; // todo: or error
}
in_game_cheats[in_game_cheats_idx] = address;
in_game_cheats_idx += 1;
in_game_cheats[in_game_cheats_idx] = value;
in_game_cheats_idx += 1;
}
}
}
_ => return INVALID_CODE_LINE,
}
} else {
//println!("bad line: '{}'", String::from_utf8_lossy(line));
return INVALID_LINE;
}
}
SUCCESS
}
/// This function is called on panic.
#[cfg(not(test))]
#[panic_handler]
@ -7,14 +243,13 @@ fn panic(_info: &core::panic::PanicInfo) -> ! {
loop {}
}
#[no_mangle]
pub extern "C" fn get_rust_u8() -> u8 {
//9
unsafe { get_c_u8() }
fn hex_to_u32(str: &[u8]) -> u32 {
use core::ptr::null_mut;
unsafe { strtoul(str.as_ptr(), null_mut(), 16) }
}
extern "C" {
fn get_c_u8() -> u8;
fn strtoul(s: *const u8, endp: *mut *mut u8, base: i32) -> u32; // todo: is base i32 ?
}
#[cfg(test)]
@ -22,7 +257,101 @@ mod tests {
use crate::*;
#[test]
fn it_works() {
assert_eq!(get_rust_u8(), 9);
fn test_hex_to_u32() {
assert_eq!(hex_to_u32(b"0x09"), 9);
}
#[test]
fn test_trim_start() {
let buf = &b" bla"[..];
let buf = buf.trim_start(WHITESPACE);
assert_eq!(buf, b"bla");
let buf = &b"\n\t\r \rbla"[..];
let buf = buf.trim_start(WHITESPACE);
assert_eq!(buf, b"bla");
}
#[test]
fn test_trim_end() {
let buf = &b"bla "[..];
let buf = buf.trim_end(WHITESPACE);
assert_eq!(buf, b"bla");
let buf = &b"bla\n\t\r \r"[..];
let buf = buf.trim_end(WHITESPACE);
assert_eq!(buf, b"bla");
}
#[test]
fn test_trim() {
let buf = &b" bla "[..];
let buf = buf.trim(WHITESPACE);
assert_eq!(buf, b"bla");
let buf = &b"\n\t\r \rbla\n\t\r \r"[..];
let buf = buf.trim(WHITESPACE);
assert_eq!(buf, b"bla");
}
#[test]
fn test_lines() {
let lines = &b"\
line1
line2\r
line3
"[..];
let mut lines = lines.lines(b"\n");
assert_eq!(lines.next(), Some(&b"line1"[..]));
assert_eq!(lines.next(), Some(&b"line2\r"[..]));
assert_eq!(lines.next(), Some(&b"line3"[..]));
assert_eq!(lines.next(), None);
assert_eq!(lines.next(), None);
let lines = &b"F10004E4 2400"[..];
let mut lines = lines.lines(b" ");
assert_eq!(lines.next(), Some(&b"F10004E4"[..]));
assert_eq!(lines.next(), Some(&b"2400"[..]));
assert_eq!(lines.next(), None);
}
#[test]
fn test_parse_cheats() {
let cheats_file = &b"\
---
A:
- F10004E4 2400
- EE000000 0000
B:
- 8138EDA0 2400
"[..];
let mut boot_cheats = [0u32; 6];
let mut in_game_cheats = [0u32; 6];
let ok = parse_cheats(cheats_file, &mut boot_cheats, &mut in_game_cheats);
assert_eq!(ok, SUCCESS);
assert_eq!(boot_cheats, [0xF10004E4, 0x2400, 0xEE000000, 0x0000, 0, 0]);
assert_eq!(in_game_cheats, [0x8138EDA0, 0x2400, 0, 0, 0, 0]);
let cheats_file = &b"\
---
# Legend of Zelda, The - Ocarina of Time (USA) (Rev 2)
Master Code:
- F10004E4 2400
- EE000000 0000
In Health (ASM):
- 8138EDA0 2400
"[..];
let mut boot_cheats = [0u32; 6];
let mut in_game_cheats = [0u32; 6];
let ok = parse_cheats(cheats_file, &mut boot_cheats, &mut in_game_cheats);
assert_eq!(ok, SUCCESS);
assert_eq!(boot_cheats, [0xF10004E4, 0x2400, 0xEE000000, 0x0000, 0, 0]);
assert_eq!(in_game_cheats, [0x8138EDA0, 0x2400, 0, 0, 0, 0]);
}
}

View File

@ -37,10 +37,13 @@
#include "sound.h"
#include "mp3.h"
#ifdef USE_YAML
// YAML parser
#include <yaml.h>
#else
#include <altra64.h>
#endif /* USE_YAML */
#include "constants.h"
#include "debug.h"
@ -2028,77 +2031,26 @@ void readSDcard(display_context_t disp, char *directory)
display_dir(list, cursor, page, MAX_LIST, count, disp);
}
/*
* Returns two cheat lists:
* - One for the "at boot" cheats
* - Another for the "in-game" cheats
*/
int readCheatFile(TCHAR *filename, u32 *cheat_lists[2])
{
#ifdef USE_YAML
int parse_cheats_yaml(char *cheatfile, u32 *list1, u32 *list2) {
// YAML parser
yaml_parser_t parser;
yaml_event_t event;
// State for YAML parser
int is_code = 0;
int code_on = 1;
int done = 0;
u32 *list1;
u32 *list2;
char *next;
int repeater = 0;
u32 address;
u32 value;
yaml_parser_initialize(&parser);
FRESULT result;
FIL file;
UINT bytesread;
result = f_open(&file, filename, FA_READ);
if (result == FR_OK)
{
int fsize = f_size(&file);
char *cheatfile = malloc(fsize);
if (!cheatfile)
{
return -2; // Out of memory
}
/*
* Size of the cheat list can never be more than half the size of the YAML
* Minimum YAML example:
* A:-80001234 FFFF
* Which is exactly 16 bytes.
* The cheat list in this case fits into exactly 8 bytes (2 words):
* 0x80001234, 0x0000FFFF
*/
list1 = calloc(1, fsize + 2 * sizeof(u32)); // Plus 2 words to be safe
if (!list1)
{
// Free
free(cheatfile);
return -2; // Out of memory
}
list2 = &list1[fsize / sizeof(u32) / 2];
cheat_lists[0] = list1;
cheat_lists[1] = list2;
result =
f_read (
&file, /* [IN] File object */
cheatfile, /* [OUT] Buffer to store read data */
fsize, /* [IN] Number of bytes to read */
&bytesread /* [OUT] Number of bytes read */
);
f_close(&file);
yaml_parser_set_input_string(&parser, cheatfile, strlen(cheatfile));
char *next;
int repeater = 0;
yaml_parser_initialize(&parser);
yaml_parser_set_input_string(&parser, cheatfile, strlen(cheatfile));
do
{
@ -2106,12 +2058,7 @@ int readCheatFile(TCHAR *filename, u32 *cheat_lists[2])
{
// Free
yaml_parser_delete(&parser);
yaml_event_delete(&event);
free(cheatfile);
free(cheat_lists[0]);
cheat_lists[0] = 0;
cheat_lists[1] = 0;
yaml_event_delete(&event);
return -3; // Parse error
}
@ -2223,7 +2170,84 @@ int readCheatFile(TCHAR *filename, u32 *cheat_lists[2])
// Free
yaml_parser_delete(&parser);
return repeater;
}
#endif /* USE_YAML */
/*
* Returns two cheat lists:
* - One for the "at boot" cheats
* - Another for the "in-game" cheats
*/
int readCheatFile(TCHAR *filename, u32 *cheat_lists[2])
{
u32 *list1;
u32 *list2;
int repeater = 0;
FRESULT result;
FIL file;
UINT bytesread;
result = f_open(&file, filename, FA_READ);
if (result == FR_OK)
{
int fsize = f_size(&file);
char *cheatfile = malloc(fsize);
if (!cheatfile)
{
return -2; // Out of memory
}
/*
* Size of the cheat list can never be more than half the size of the YAML
* Minimum YAML example:
* A:-80001234 FFFF
* Which is exactly 16 bytes.
* The cheat list in this case fits into exactly 8 bytes (2 words):
* 0x80001234, 0x0000FFFF
*/
size_t both_list_sizes = fsize + 2 * sizeof(u32);
list1 = calloc(1, both_list_sizes); // Plus 2 words to be safe
if (!list1)
{
// Free
free(cheatfile);
return -2; // Out of memory
}
size_t list2_size = fsize / sizeof(u32) / 2;
list2 = &list1[list2_size];
cheat_lists[0] = list1;
cheat_lists[1] = list2;
result =
f_read (
&file, /* [IN] File object */
cheatfile, /* [OUT] Buffer to store read data */
fsize, /* [IN] Number of bytes to read */
&bytesread /* [OUT] Number of bytes read */
);
f_close(&file);
#ifdef USE_YAML
repeater = parse_cheats_yaml(cheatfile, list1, list2);
#else
// use rust
size_t list1_size = both_list_sizes - list2_size;
repeater = parse_cheats_ffi(cheatfile, fsize, list1, list1_size, list2, list2_size);
#endif /* USE_YAML */
free(cheatfile);
if (repeater != 0) {
// this is what original yaml code did
free(cheat_lists[0]);
cheat_lists[0] = 0;
cheat_lists[1] = 0;
}
return repeater; // Ok or repeater error

View File

@ -2,8 +2,6 @@
#include <libdragon.h>
#include <stdio.h>
#include <altra64.h>
#include "types.h"
#include "menu.h"
#include "version.h"
@ -16,7 +14,7 @@ void menu_about(display_context_t disp)
char version_str[32];
char firmware_str[32];
sprintf(version_str, "Altra64 %d: v%s", get_rust_u8(), Altra64_GetVersionString());
sprintf(version_str, "Altra64: v%s", Altra64_GetVersionString());
printText(version_str, 9, 8, disp);
sprintf(firmware_str, "ED64 firmware: v%03x", evd_getFirmVersion());
printText(firmware_str, 9, -1, disp);