mirror of
https://github.com/open-goal/jak-project
synced 2026-08-06 09:54:10 -04:00
535 lines
20 KiB
Common Lisp
535 lines
20 KiB
Common Lisp
;;-*-Lisp-*-
|
|
(in-package goal)
|
|
(bundles "KERNEL.CGO")
|
|
(require "kernel/gstring-h.gc")
|
|
(require "kernel/gcommon.gc")
|
|
|
|
;; The GOAL string type is like a C string plus a length field.
|
|
;; The number of bytes stored is the length + 1 for the null terminator.
|
|
;; Note that string is a bit of a special type, and the compiler assumes there is no
|
|
;; child type of string ever created.
|
|
|
|
;; DECOMP BEGINS
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; String methods
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(defmethod length ((this string))
|
|
"Return the number of bytes before the null terminator."
|
|
(let ((cursor (-> this data)))
|
|
(while (nonzero? (-> cursor 0))
|
|
(nop!)
|
|
(nop!)
|
|
(nop!)
|
|
(set! cursor (&-> cursor 1)))
|
|
(&- cursor (the-as uint (-> this data)))))
|
|
|
|
(defmethod asize-of ((this string))
|
|
"Return the string header, allocated byte capacity, and null terminator size."
|
|
(+ (-> this allocated-length) 1 (-> string size)))
|
|
|
|
(defun copy-string<-string ((dst string) (src string))
|
|
"Copy src and its null terminator into dst without checking capacity."
|
|
(let ((dst-ptr (-> dst data)))
|
|
(let ((src-ptr (-> src data)))
|
|
(while (nonzero? (-> src-ptr 0))
|
|
(set! (-> dst-ptr 0) (-> src-ptr 0))
|
|
(set! dst-ptr (&-> dst-ptr 1))
|
|
(set! src-ptr (&-> src-ptr 1))))
|
|
(set! (-> dst-ptr 0) (the-as uint 0)))
|
|
dst)
|
|
|
|
(defun copyn-string<-string ((dst string) (src string) (len int))
|
|
"Copy len bytes from src when it contains at least len bytes, then terminate dst."
|
|
(let ((dst-ptr (-> dst data))
|
|
(src-ptr (-> src data)))
|
|
(when (<= len (length src))
|
|
(dotimes (i len)
|
|
(set! (-> dst-ptr) (-> src-ptr))
|
|
(&+! dst-ptr 1)
|
|
(&+! src-ptr 1)))
|
|
(set! (-> dst-ptr 0) 0))
|
|
dst)
|
|
|
|
(defun substring! ((dst string) (src string) (start int) (end int))
|
|
"Copy the half-open byte range [start, end) to dst, or return an empty string for invalid indices."
|
|
(let ((strlen (length src)))
|
|
(if (and (>= start 0) (<= start end) (<= start strlen) (<= end strlen))
|
|
(let ((start-ptr (-> src data))
|
|
(dst-ptr (-> dst data)))
|
|
(dotimes (i (- end start))
|
|
(set! (-> dst-ptr) (-> start-ptr (+ start i)))
|
|
(&+! dst-ptr 1))
|
|
(set! (-> dst-ptr 0) 0)
|
|
dst)
|
|
"")))
|
|
|
|
(defmethod new string ((allocation symbol) (type-to-make type) (size int) (other string))
|
|
"Allocate a string with at least size bytes, copying other when supplied."
|
|
(cond
|
|
(other
|
|
(let* ((desired-size (max (length other) size))
|
|
(new-string (object-new allocation type-to-make (+ desired-size 1 (-> type-to-make size)))))
|
|
(set! (-> new-string allocated-length) desired-size)
|
|
(copy-string<-string new-string other)))
|
|
(else
|
|
(let ((new-string (object-new allocation type-to-make (+ size 1 (-> type-to-make size)))))
|
|
(set! (-> new-string allocated-length) size)
|
|
new-string))))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; String comparison
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(defun string= ((str-a string) (str-b string))
|
|
"Return true when two non-null GOAL strings contain the same bytes."
|
|
(let ((a-ptr (-> str-a data))
|
|
(b-ptr (-> str-b data)))
|
|
(if (or (zero? str-a) (zero? str-b)) (return #f))
|
|
(while (and (nonzero? (-> a-ptr 0)) (nonzero? (-> b-ptr 0)))
|
|
(if (!= (-> a-ptr 0) (-> b-ptr 0)) (return #f))
|
|
(set! a-ptr (&-> a-ptr 1))
|
|
(set! b-ptr (&-> b-ptr 1)))
|
|
(and (zero? (-> a-ptr 0)) (zero? (-> b-ptr 0)))))
|
|
|
|
(defun string-charp= ((str string) (charp (pointer uint8)))
|
|
"Return true when a GOAL string and null-terminated byte string contain the same bytes."
|
|
(let ((str-ptr (-> str data)))
|
|
(while (and (nonzero? (-> str-ptr 0)) (nonzero? (-> charp 0)))
|
|
(if (!= (-> str-ptr 0) (-> charp 0)) (return #f))
|
|
(set! str-ptr (&-> str-ptr 1))
|
|
(set! charp (&-> charp 1)))
|
|
(and (zero? (-> str-ptr 0)) (zero? (-> charp 0)))))
|
|
|
|
(defun name= ((left basic) (right basic))
|
|
"Compare two strings or symbols by name."
|
|
(cond
|
|
((= left right) #t)
|
|
((and (= (-> left type) string) (= (-> right type) string)) (string= (the-as string left) (the-as string right)))
|
|
((and (= (-> left type) string) (= (-> right type) symbol))
|
|
(string= (the-as string left) (symbol->string (the-as symbol right))))
|
|
((and (= (-> right type) string) (= (-> left type) symbol))
|
|
(string= (the-as string right) (symbol->string (the-as symbol left))))))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; String copying
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(defun copyn-string<-charp ((str string) (charp (pointer uint8)) (len int))
|
|
"Copy exactly len bytes from a C string and append a null terminator."
|
|
(let ((str-ptr (-> str data)))
|
|
(dotimes (i len)
|
|
(set! (-> str-ptr 0) (-> charp 0))
|
|
(set! str-ptr (&-> str-ptr 1))
|
|
(set! charp (&-> charp 1)))
|
|
(set! (-> str-ptr 0) (the-as uint 0)))
|
|
str)
|
|
|
|
(defun string<-charp ((str string) (charp (pointer uint8)))
|
|
"Copy a null-terminated byte string into a GOAL string without checking capacity."
|
|
(let ((str-ptr (-> str data)))
|
|
(while (nonzero? (-> charp 0))
|
|
(set! (-> str-ptr 0) (-> charp 0))
|
|
(set! str-ptr (&-> str-ptr 1))
|
|
(set! charp (&-> charp 1)))
|
|
(set! (-> str-ptr 0) (the-as uint 0)))
|
|
str)
|
|
|
|
(defun charp<-string ((charp (pointer uint8)) (str string))
|
|
"Copy a GOAL string and its null terminator into a byte buffer."
|
|
(let ((str-ptr (-> str data)))
|
|
(while (nonzero? (-> str-ptr 0))
|
|
(set! (-> charp 0) (-> str-ptr 0))
|
|
(set! charp (&-> charp 1))
|
|
(set! str-ptr (&-> str-ptr 1))))
|
|
(set! (-> charp 0) (the-as uint 0))
|
|
0)
|
|
|
|
(defun copy-charp<-charp ((dst (pointer uint8)) (src (pointer uint8)))
|
|
"Copy a null-terminated byte string and return its destination terminator."
|
|
(while (nonzero? (-> src 0))
|
|
(set! (-> dst 0) (-> src 0))
|
|
(set! dst (&-> dst 1))
|
|
(set! src (&-> src 1)))
|
|
(set! (-> dst 0) (the-as uint 0))
|
|
dst)
|
|
|
|
(defun cat-string<-string ((a string) (b string))
|
|
"Append b to a without checking capacity."
|
|
(let ((a-ptr (-> a data)))
|
|
(let ((b-ptr (-> b data)))
|
|
(while (nonzero? (-> a-ptr 0))
|
|
(nop!)
|
|
(nop!)
|
|
(nop!)
|
|
(set! a-ptr (&-> a-ptr 1)))
|
|
(while (nonzero? (-> b-ptr 0))
|
|
(set! (-> a-ptr 0) (-> b-ptr 0))
|
|
(set! a-ptr (&-> a-ptr 1))
|
|
(set! b-ptr (&-> b-ptr 1))))
|
|
(set! (-> a-ptr 0) (the-as uint 0)))
|
|
a)
|
|
|
|
(defun catn-string<-charp ((a string) (b (pointer uint8)) (len int))
|
|
"Append exactly len bytes from b to a and add a null terminator."
|
|
(let ((a-ptr (-> a data)))
|
|
(while (nonzero? (-> a-ptr 0))
|
|
(nop!)
|
|
(nop!)
|
|
(nop!)
|
|
(set! a-ptr (&-> a-ptr 1)))
|
|
(dotimes (i len)
|
|
(set! (-> a-ptr 0) (-> b 0))
|
|
(set! a-ptr (&-> a-ptr 1))
|
|
(set! b (&-> b 1)))
|
|
(set! (-> a-ptr 0) (the-as uint 0)))
|
|
a)
|
|
|
|
(defun cat-string<-string_to_charp ((a string) (b string) (end-ptr (pointer uint8)))
|
|
"Append bytes from b through end-ptr inclusive, stopping earlier at b's null terminator. Return the new terminator."
|
|
(let ((b-ptr (-> b data))
|
|
(a-ptr (-> a data)))
|
|
(while (nonzero? (-> a-ptr 0))
|
|
(nop!)
|
|
(nop!)
|
|
(nop!)
|
|
(set! a-ptr (&-> a-ptr 1)))
|
|
(while (and (>= (the-as int end-ptr) (the-as int b-ptr)) (nonzero? (-> b-ptr 0)))
|
|
(set! (-> a-ptr 0) (-> b-ptr 0))
|
|
(set! a-ptr (&-> a-ptr 1))
|
|
(set! b-ptr (&-> b-ptr 1)))
|
|
(set! (-> a-ptr 0) (the-as uint 0))
|
|
a-ptr))
|
|
|
|
(defun append-character-to-string ((str string) (char uint8))
|
|
"Append one byte and a new null terminator without checking capacity."
|
|
(let ((str-ptr (-> str data)))
|
|
(while (nonzero? (-> str-ptr 0))
|
|
(nop!)
|
|
(nop!)
|
|
(nop!)
|
|
(set! str-ptr (&-> str-ptr 1)))
|
|
(set! (-> str-ptr 0) (the-as uint char))
|
|
(set! (-> str-ptr 1) (the-as uint 0)))
|
|
0)
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; String utilities
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(defun string-upcase ((in string) (out string))
|
|
"Convert ASCII lowercase letters from in to uppercase in out."
|
|
(let* ((in-ptr (-> in data))
|
|
(in-data (-> in-ptr 0))
|
|
(in-idx 1)
|
|
(out-idx 0))
|
|
(while (nonzero? in-data)
|
|
(if (and (>= in-data (the-as uint 97)) (>= (the-as uint 122) in-data)) (+! in-data -32))
|
|
(set! (-> out data out-idx) in-data)
|
|
(set! in-data (-> in-ptr in-idx))
|
|
(+! in-idx 1)
|
|
(+! out-idx 1))
|
|
(set! (-> out data out-idx) (the-as uint 0)))
|
|
out)
|
|
|
|
(defun string-downcase ((in string) (out string))
|
|
"Convert ASCII uppercase letters from in to lowercase in out."
|
|
(let* ((in-ptr (-> in data))
|
|
(in-data (-> in-ptr 0))
|
|
(in-idx 1)
|
|
(out-idx 0))
|
|
(while (nonzero? in-data)
|
|
(if (and (>= in-data (the-as uint 65)) (>= (the-as uint 90) in-data)) (+! in-data 32))
|
|
(set! (-> out data out-idx) in-data)
|
|
(set! in-data (-> in-ptr in-idx))
|
|
(+! in-idx 1)
|
|
(+! out-idx 1))
|
|
(set! (-> out data out-idx) (the-as uint 0)))
|
|
out)
|
|
|
|
(defun charp-basename ((charp (pointer uint8)))
|
|
"Return the bytes after the final slash or backslash, or the original pointer when neither occurs."
|
|
(let ((ptr charp))
|
|
(while (nonzero? (-> ptr 0))
|
|
(set! ptr (&-> ptr 1)))
|
|
(while (< (the-as int charp) (the-as int ptr))
|
|
(set! ptr (&-> ptr -1))
|
|
(if (or (= (-> ptr 0) 47) (= (-> ptr 0) 92)) (return (&-> ptr 1)))))
|
|
charp)
|
|
|
|
(defun clear ((str string))
|
|
"Make a string empty and return it."
|
|
(set! (-> str data 0) (the-as uint 0))
|
|
str)
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; String ordering
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;; NOTE: these string comparisons are a little broken.
|
|
;; ex: (string<? "asd" "asdf") = #f
|
|
;; (string<? "asdf" "asd") = #f
|
|
;; these comparisons do not properly order strings.
|
|
|
|
(defun string<? ((a string) (b string))
|
|
"Compare the shared prefix bytewise and return true when a first differing byte in a is smaller. A strict prefix is not considered smaller."
|
|
(let ((len (min (length a) (length b))))
|
|
(dotimes (i len)
|
|
(cond
|
|
((< (-> a data i) (-> b data i)) (return #t))
|
|
((< (-> b data i) (-> a data i)) (return #f)))))
|
|
#f)
|
|
|
|
(defun string>? ((a string) (b string))
|
|
"Compare the shared prefix bytewise and return true when a first differing byte in a is larger. A strict prefix is not considered larger."
|
|
(let ((len (min (length a) (length b))))
|
|
(dotimes (i len)
|
|
(cond
|
|
((< (-> a data i) (-> b data i)) (return #f))
|
|
((< (-> b data i) (-> a data i)) (return #t)))))
|
|
#f)
|
|
|
|
(defun string<=? ((a string) (b string))
|
|
"Compare the shared prefix bytewise and return false only when a first differing byte in a is larger."
|
|
(let ((len (min (length a) (length b))))
|
|
(dotimes (i len)
|
|
(cond
|
|
((< (-> a data i) (-> b data i)) (return #t))
|
|
((< (-> b data i) (-> a data i)) (return #f)))))
|
|
#t)
|
|
|
|
(defun string>=? ((a string) (b string))
|
|
"Compare the shared prefix bytewise and return false only when a first differing byte in a is smaller."
|
|
(let ((len (min (length a) (length b))))
|
|
(dotimes (i len)
|
|
(cond
|
|
((< (-> a data i) (-> b data i)) (return #f))
|
|
((< (-> b data i) (-> a data i)) (return #t)))))
|
|
#t)
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; String argument parsing
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;; temporary string for argument functions
|
|
(define *string-tmp-str* (new 'global 'string 128 (the-as string #f)))
|
|
|
|
(defun string-skip-to-char ((cursor (pointer uint8)) (char uint))
|
|
"Return the first occurrence of char, or the null terminator when it is absent."
|
|
(while (and (nonzero? (-> cursor 0)) (!= (-> cursor 0) char))
|
|
(set! cursor (&-> cursor 1)))
|
|
cursor)
|
|
|
|
(defun string-cat-to-last-char ((base-str string) (append-str string) (char uint))
|
|
"Append append-str through its final occurrence of char, or append nothing when char is absent."
|
|
(let ((end-of-append (&-> (the-as (pointer uint8) append-str) 3)))
|
|
(let ((location-of-char (string-skip-to-char (-> append-str data) char)))
|
|
(when (= (-> location-of-char 0) char)
|
|
(until (!= (-> location-of-char 0) char)
|
|
(set! end-of-append location-of-char)
|
|
(set! location-of-char (string-skip-to-char (&-> location-of-char 1) char)))))
|
|
(cat-string<-string_to_charp base-str append-str end-of-append)))
|
|
|
|
(defmacro is-whitespace-char? (c)
|
|
;; 32 = space
|
|
;; 9 = \t
|
|
;; 13 = \r
|
|
;; 10 = \n
|
|
`(or (= ,c 32) (= ,c 9) (= ,c 13) (= ,c 10)))
|
|
|
|
(defmacro not-whitespace-char? (c)
|
|
`(not (is-whitespace-char? ,c)))
|
|
|
|
(defun string-skip-whitespace ((cursor (pointer uint8)))
|
|
"Return the first byte not containing space, tab, carriage return, or newline."
|
|
(while (and (nonzero? (-> cursor 0)) (is-whitespace-char? (-> cursor 0)))
|
|
(set! cursor (&-> cursor 1)))
|
|
cursor)
|
|
|
|
(defun string-suck-up! ((str string) (location (pointer uint8)))
|
|
"Remove every byte before location by moving the remaining suffix to the start of str."
|
|
(when (!= location (-> str data))
|
|
(let ((str-ptr (-> str data)))
|
|
(while (nonzero? (-> location 0))
|
|
(set! (-> str-ptr 0) (-> location 0))
|
|
(set! str-ptr (&-> str-ptr 1))
|
|
(set! location (&-> location 1)))
|
|
(set! (-> str-ptr 0) (the-as uint 0)))
|
|
0)
|
|
#f)
|
|
|
|
(defun string-strip-leading-whitespace! ((str string))
|
|
"Remove spaces, tabs, carriage returns, and newlines from the start of a string."
|
|
(let ((content-start (string-skip-whitespace (-> str data)))) (string-suck-up! str content-start))
|
|
#f)
|
|
|
|
(defun string-strip-trailing-whitespace! ((str string))
|
|
"Remove spaces, tabs, carriage returns, and newlines from the end of a string."
|
|
(when (nonzero? (length str))
|
|
(let ((ptr (&+ (-> str data) (+ (length str) -1))))
|
|
(while (and (>= (the-as int ptr) (the-as int (-> str data))) (is-whitespace-char? (-> ptr 0)))
|
|
(set! ptr (&-> ptr -1)))
|
|
(set! (-> ptr 1) (the-as uint 0)))
|
|
0)
|
|
#f)
|
|
|
|
(defun string-strip-whitespace! ((str string))
|
|
"Remove spaces, tabs, carriage returns, and newlines from both ends of a string."
|
|
(string-strip-trailing-whitespace! str)
|
|
(string-strip-leading-whitespace! str)
|
|
#f)
|
|
|
|
(defun string-get-arg!! ((a-str string) (arg string))
|
|
"Remove and copy the first whitespace-delimited argument. Quoted arguments may contain whitespace."
|
|
(let ((arg-word-start (string-skip-whitespace (-> arg data))))
|
|
(cond
|
|
((= (-> arg-word-start 0) 34)
|
|
(let ((arg-end (&-> arg-word-start 1)))
|
|
(let ((arg-start arg-end))
|
|
(while (and (nonzero? (-> arg-end 0)) (!= (-> arg-end 0) 34))
|
|
(set! arg-end (&-> arg-end 1)))
|
|
(copyn-string<-charp a-str arg-start (&- arg-end (the-as uint arg-start))))
|
|
(if (= (-> arg-end 0) 34) (set! arg-end (&-> arg-end 1)))
|
|
(let ((next-arg (string-skip-whitespace arg-end))) (string-suck-up! arg next-arg)))
|
|
(return #t))
|
|
((nonzero? (-> arg-word-start 0))
|
|
(let ((arg-start arg-word-start))
|
|
(while (and (nonzero? (-> arg-word-start 0))
|
|
(!= (-> arg-word-start 0) 32)
|
|
(!= (-> arg-word-start 0) 9)
|
|
(!= (-> arg-word-start 0) 13)
|
|
(!= (-> arg-word-start 0) 10))
|
|
(set! arg-word-start (&-> arg-word-start 1)))
|
|
(copyn-string<-charp a-str arg-start (&- arg-word-start (the-as uint arg-start))))
|
|
(let ((next-arg (string-skip-whitespace arg-word-start))) (string-suck-up! arg next-arg))
|
|
(return #t))))
|
|
#f)
|
|
|
|
(defun string->int ((str string))
|
|
"Parse a decimal, #x hexadecimal, or #b binary integer. Parsing stops at the first invalid digit."
|
|
(let ((str-ptr (-> str data))
|
|
(result 0)
|
|
(negate #f))
|
|
(cond
|
|
((= (-> str-ptr 0) 35)
|
|
(let ((next-char-1 (&-> str-ptr 1)))
|
|
(cond
|
|
((or (= (-> next-char-1 0) 120) (= (-> next-char-1 0) 88))
|
|
(let ((next-char-2 (&-> next-char-1 1)))
|
|
(when (= (-> next-char-2 1) 45)
|
|
(set! negate #t)
|
|
(set! next-char-2 (&-> next-char-2 1)))
|
|
(while (or (and (>= (-> next-char-2 0) (the-as uint 48)) (>= (the-as uint 57) (-> next-char-2 0)))
|
|
(and (>= (-> next-char-2 0) (the-as uint 65)) (>= (the-as uint 70) (-> next-char-2 0)))
|
|
(and (>= (-> next-char-2 0) (the-as uint 97)) (>= (the-as uint 102) (-> next-char-2 0))))
|
|
(cond
|
|
((and (>= (-> next-char-2 0) (the-as uint 65)) (>= (the-as uint 70) (-> next-char-2 0)))
|
|
(set! result (the-as int (+ (-> next-char-2 0) -55 (* result 16)))))
|
|
((and (>= (-> next-char-2 0) (the-as uint 97)) (>= (the-as uint 102) (-> next-char-2 0)))
|
|
(set! result (the-as int (+ (-> next-char-2 0) -87 (* result 16)))))
|
|
(else (set! result (the-as int (+ (-> next-char-2 0) -48 (* result 16))))))
|
|
(set! next-char-2 (&-> next-char-2 1)))))
|
|
((or (= (-> next-char-1 0) 98) (= (-> next-char-1 0) 66))
|
|
(let ((a0-4 (&-> next-char-1 1)))
|
|
(while (and (>= (-> a0-4 0) (the-as uint 48)) (>= (the-as uint 49) (-> a0-4 0)))
|
|
(set! result (the-as int (+ (-> a0-4 0) -48 (* result 2))))
|
|
(set! a0-4 (&-> a0-4 1))))))))
|
|
(else
|
|
(when (= (-> str-ptr 1) 45)
|
|
(set! negate #t)
|
|
(set! str-ptr (&-> str-ptr 1)))
|
|
(while (and (>= (-> str-ptr 0) (the-as uint 48)) (>= (the-as uint 57) (-> str-ptr 0)))
|
|
(set! result (the-as int (+ (-> str-ptr 0) -48 (* 10 result))))
|
|
(set! str-ptr (&-> str-ptr 1)))))
|
|
(cond
|
|
(negate (- result))
|
|
(else (empty) result))))
|
|
|
|
(defun string->float ((str string))
|
|
"Convert a decimal string to a float. The original EE implementation reports that conversion is unsupported."
|
|
(#unless PC_PORT
|
|
(format 0 "string->float left as an excersize for the reader~%")
|
|
0.0)
|
|
(#when PC_PORT
|
|
(let ((charp (-> str data))
|
|
(result 0.0)
|
|
(negative #f))
|
|
(when (= (-> charp) #\-)
|
|
(set! negative #t)
|
|
(&+! charp 1))
|
|
(while (and (>= (-> charp) #\0) (<= (-> charp) #\9))
|
|
(*! result 10)
|
|
(+! result (- (-> charp) #\0))
|
|
(&+! charp 1))
|
|
(when (= (-> charp) #\.)
|
|
(&+! charp 1)
|
|
(let ((frac 1.0))
|
|
(while (and (>= (-> charp) #\0) (<= (-> charp) #\9))
|
|
(*! frac 10)
|
|
(+! result (/ (the float (- (-> charp) #\0)) frac))
|
|
(&+! charp 1))))
|
|
(cond
|
|
(negative (- result))
|
|
(else result)))))
|
|
|
|
(defun string-get-int32!! ((result (pointer int32)) (args string))
|
|
"Consume the next argument, parse it as an integer, and store it in result."
|
|
(cond
|
|
((string-get-arg!! *string-tmp-str* args) (set! (-> result 0) (string->int *string-tmp-str*)) #t)
|
|
(else #f)))
|
|
|
|
(defun string-get-float!! ((result (pointer float)) (args string))
|
|
"Consume the next argument, parse it as a float, and store it in result."
|
|
(cond
|
|
((string-get-arg!! *string-tmp-str* args) (set! (-> result 0) (string->float *string-tmp-str*)) #t)
|
|
(else #f)))
|
|
|
|
(defun string-get-flag!! ((result (pointer symbol)) (in string) (first-flag string) (second-flag string))
|
|
"Consume the next argument when it matches either flag; store true for first-flag and false for second-flag."
|
|
(cond
|
|
((string-get-arg!! *string-tmp-str* in)
|
|
(cond
|
|
((or (string= *string-tmp-str* first-flag) (string= *string-tmp-str* second-flag))
|
|
(set! (-> result 0) (string= *string-tmp-str* first-flag))
|
|
#t)
|
|
(else #f)))
|
|
(else #f)))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;
|
|
;; Globals
|
|
;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;; The *stdcon* buffer holds text to be printed to the screen for debugging.
|
|
;; Under the hood, there are actually two buffers: *stdcon1* and *stdcon0*
|
|
|
|
;; The *stdcon0* buffer is the default and is cleared on each frame.
|
|
;; The *stdcon1* buffer is cleared only if the game is unpaused. This keeps text
|
|
;; on screen for game objects, even when the game is paused and their logic does not run.
|
|
|
|
;; The kernel takes care of setting *stdcon* to one of these two buffers as needed.
|
|
|
|
(define *debug-draw-pauseable* #f)
|
|
|
|
(define *stdcon0* (new 'global 'string 16384 (the string #f)))
|
|
|
|
(define *stdcon1* (new 'global 'string 16384 (the string #f)))
|
|
|
|
(define *stdcon* *stdcon0*)
|
|
|
|
;; shared temporary strings.
|
|
(define *temp-string* (new 'global 'string 256 (the string #f)))
|
|
|
|
(define *temp-string2* (new 'global 'string 256 (the string #f)))
|
|
|
|
(define *pc-cpp-temp-string*
|
|
"A convenient place to retrieve a string from C++"
|
|
(new 'global 'string 256 (the-as string #f)))
|
|
|
|
(defmacro string-format (&rest args)
|
|
"Formats into *temp-string* and returns it, for in-place string formating.
|
|
DO NOT USE *temp-string* WITH THIS MACRO! It is read as input AFTER all of the args evaluate."
|
|
`(begin
|
|
(format (clear *temp-string*) ,@args)
|
|
*temp-string*))
|