mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
Implement READFILE function (#1759)
This commit is contained in:
13
man/rgbasm.5
13
man/rgbasm.5
@@ -599,6 +599,7 @@ with its corresponding argument in
|
|||||||
.Pq So %% Sc is replaced by the So % Sc character .
|
.Pq So %% Sc is replaced by the So % Sc character .
|
||||||
.It Fn STRCHAR str idx Ta Returns the substring of Ar str No for the charmap entry at Ar idx No with the current charmap . Pq Ar idx No counts charmap entries, not characters.
|
.It Fn STRCHAR str idx Ta Returns the substring of Ar str No for the charmap entry at Ar idx No with the current charmap . Pq Ar idx No counts charmap entries, not characters.
|
||||||
.It Fn REVCHAR vals... Ta Returns the string that is mapped to Ar vals No with the current charmap. If there is no unique charmap entry for Ar vals Ns , an error occurs.
|
.It Fn REVCHAR vals... Ta Returns the string that is mapped to Ar vals No with the current charmap. If there is no unique charmap entry for Ar vals Ns , an error occurs.
|
||||||
|
.It Fn READFILE name max Ta Returns the contents of the file Ar name No as a string. Reads up to Ar max No bytes, or the entire contents if Ar max No is not specified. If the file isn't found in the current directory, the include-path list passed to Xr rgbasm 1 Ap s Fl I No option on the command line will be searched.
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
The following functions operate on string expressions, but return integers.
|
The following functions operate on string expressions, but return integers.
|
||||||
@@ -1814,10 +1815,9 @@ Use
|
|||||||
.Ic INCBIN
|
.Ic INCBIN
|
||||||
to include a raw binary file as it is.
|
to include a raw binary file as it is.
|
||||||
If the file isn't found in the current directory, the include-path list passed to
|
If the file isn't found in the current directory, the include-path list passed to
|
||||||
.Xr rgbasm 1
|
.Xr rgbasm 1 Ap s
|
||||||
(see the
|
|
||||||
.Fl I
|
.Fl I
|
||||||
option) on the command line will be searched.
|
option on the command line will be searched.
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
INCBIN "titlepic.bin"
|
INCBIN "titlepic.bin"
|
||||||
INCBIN "sprites/hero.bin"
|
INCBIN "sprites/hero.bin"
|
||||||
@@ -2370,11 +2370,10 @@ block, all of them but the first one are ignored.
|
|||||||
Use
|
Use
|
||||||
.Ic INCLUDE
|
.Ic INCLUDE
|
||||||
to process another assembler file and then return to the current file when done.
|
to process another assembler file and then return to the current file when done.
|
||||||
If the file isn't found in the current directory, the include path list (see the
|
If the file isn't found in the current directory, the include-path list passed to
|
||||||
|
.Xr rgbasm 1 Ap s
|
||||||
.Fl I
|
.Fl I
|
||||||
option in
|
option on the command line will be searched.
|
||||||
.Xr rgbasm 1 )
|
|
||||||
will be searched.
|
|
||||||
You may nest
|
You may nest
|
||||||
.Ic INCLUDE
|
.Ic INCLUDE
|
||||||
calls infinitely (or until you run out of memory, whichever comes first).
|
calls infinitely (or until you run out of memory, whichever comes first).
|
||||||
|
|||||||
@@ -239,6 +239,7 @@ static std::unordered_map<std::string, int, CaseInsensitive, CaseInsensitive> ke
|
|||||||
{"TZCOUNT", T_(OP_TZCOUNT) },
|
{"TZCOUNT", T_(OP_TZCOUNT) },
|
||||||
|
|
||||||
{"BYTELEN", T_(OP_BYTELEN) },
|
{"BYTELEN", T_(OP_BYTELEN) },
|
||||||
|
{"READFILE", T_(OP_READFILE) },
|
||||||
{"STRBYTE", T_(OP_STRBYTE) },
|
{"STRBYTE", T_(OP_STRBYTE) },
|
||||||
{"STRCAT", T_(OP_STRCAT) },
|
{"STRCAT", T_(OP_STRCAT) },
|
||||||
{"STRCHAR", T_(OP_STRCHAR) },
|
{"STRCHAR", T_(OP_STRCHAR) },
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
#include <optional>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -61,6 +62,7 @@
|
|||||||
|
|
||||||
yy::parser::symbol_type yylex(); // Provided by lexer.cpp
|
yy::parser::symbol_type yylex(); // Provided by lexer.cpp
|
||||||
|
|
||||||
|
static std::optional<std::string> readFile(std::string const &name, uint32_t maxLen);
|
||||||
static uint32_t strToNum(std::vector<int32_t> const &s);
|
static uint32_t strToNum(std::vector<int32_t> const &s);
|
||||||
static void errorInvalidUTF8Byte(uint8_t byte, char const *functionName);
|
static void errorInvalidUTF8Byte(uint8_t byte, char const *functionName);
|
||||||
static size_t strlenUTF8(std::string const &str, bool printErrors);
|
static size_t strlenUTF8(std::string const &str, bool printErrors);
|
||||||
@@ -303,6 +305,7 @@
|
|||||||
%token OP_LOG "LOG"
|
%token OP_LOG "LOG"
|
||||||
%token OP_LOW "LOW"
|
%token OP_LOW "LOW"
|
||||||
%token OP_POW "POW"
|
%token OP_POW "POW"
|
||||||
|
%token OP_READFILE "READFILE"
|
||||||
%token OP_REVCHAR "REVCHAR"
|
%token OP_REVCHAR "REVCHAR"
|
||||||
%token OP_ROUND "ROUND"
|
%token OP_ROUND "ROUND"
|
||||||
%token OP_SIN "SIN"
|
%token OP_SIN "SIN"
|
||||||
@@ -1687,6 +1690,20 @@ string_literal:
|
|||||||
$$ = std::move($1);
|
$$ = std::move($1);
|
||||||
$$.append($3);
|
$$.append($3);
|
||||||
}
|
}
|
||||||
|
| OP_READFILE LPAREN string RPAREN {
|
||||||
|
if (std::optional<std::string> contents = readFile($3, UINT32_MAX); contents) {
|
||||||
|
$$ = std::move(*contents);
|
||||||
|
} else {
|
||||||
|
YYACCEPT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
| OP_READFILE LPAREN string COMMA uconst RPAREN {
|
||||||
|
if (std::optional<std::string> contents = readFile($3, $5); contents) {
|
||||||
|
$$ = std::move(*contents);
|
||||||
|
} else {
|
||||||
|
YYACCEPT;
|
||||||
|
}
|
||||||
|
}
|
||||||
| OP_STRSLICE LPAREN string COMMA iconst COMMA iconst RPAREN {
|
| OP_STRSLICE LPAREN string COMMA iconst COMMA iconst RPAREN {
|
||||||
size_t len = strlenUTF8($3, false);
|
size_t len = strlenUTF8($3, false);
|
||||||
uint32_t start = adjustNegativeIndex($5, len, "STRSLICE");
|
uint32_t start = adjustNegativeIndex($5, len, "STRSLICE");
|
||||||
@@ -2728,6 +2745,44 @@ void yy::parser::error(std::string const &str) {
|
|||||||
::error("%s", str.c_str());
|
::error("%s", str.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::optional<std::string> readFile(std::string const &name, uint32_t maxLen) {
|
||||||
|
FILE *file = nullptr;
|
||||||
|
if (std::optional<std::string> fullPath = fstk_FindFile(name); fullPath) {
|
||||||
|
file = fopen(fullPath->c_str(), "rb");
|
||||||
|
}
|
||||||
|
if (!file) {
|
||||||
|
if (fstk_FileError(name, "READFILE")) {
|
||||||
|
// If `fstk_FileError` returned true due to `-MG`, we should abort due to a
|
||||||
|
// missing file, so return `std::nullopt`, which tells the caller to `YYACCEPT`
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
Defer closeFile{[&] { fclose(file); }};
|
||||||
|
|
||||||
|
size_t readSize = maxLen;
|
||||||
|
if (fseek(file, 0, SEEK_END) == 0) {
|
||||||
|
// If the file is seekable and shorter than the max length,
|
||||||
|
// just read as many bytes as there are
|
||||||
|
if (long fileSize = ftell(file); static_cast<size_t>(fileSize) < readSize) {
|
||||||
|
readSize = fileSize;
|
||||||
|
}
|
||||||
|
fseek(file, 0, SEEK_SET);
|
||||||
|
} else if (errno != ESPIPE) {
|
||||||
|
error("Error determining size of READFILE file '%s': %s", name.c_str(), strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string contents;
|
||||||
|
contents.resize(readSize);
|
||||||
|
|
||||||
|
if (fread(&contents[0], 1, readSize, file) < readSize || ferror(file)) {
|
||||||
|
error("Error reading READFILE file '%s': %s", name.c_str(), strerror(errno));
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return contents;
|
||||||
|
}
|
||||||
|
|
||||||
static uint32_t strToNum(std::vector<int32_t> const &s) {
|
static uint32_t strToNum(std::vector<int32_t> const &s) {
|
||||||
uint32_t length = s.size();
|
uint32_t length = s.size();
|
||||||
|
|
||||||
|
|||||||
@@ -888,7 +888,7 @@ bool sect_BinaryFile(std::string const &name, uint32_t startPos) {
|
|||||||
}
|
}
|
||||||
Defer closeFile{[&] { fclose(file); }};
|
Defer closeFile{[&] { fclose(file); }};
|
||||||
|
|
||||||
if (fseek(file, 0, SEEK_END) != -1) {
|
if (fseek(file, 0, SEEK_END) == 0) {
|
||||||
if (startPos > ftell(file)) {
|
if (startPos > ftell(file)) {
|
||||||
error("Specified start position is greater than length of file '%s'", name.c_str());
|
error("Specified start position is greater than length of file '%s'", name.c_str());
|
||||||
return false;
|
return false;
|
||||||
@@ -935,7 +935,7 @@ bool sect_BinaryFileSlice(std::string const &name, uint32_t startPos, uint32_t l
|
|||||||
}
|
}
|
||||||
Defer closeFile{[&] { fclose(file); }};
|
Defer closeFile{[&] { fclose(file); }};
|
||||||
|
|
||||||
if (fseek(file, 0, SEEK_END) != -1) {
|
if (fseek(file, 0, SEEK_END) == 0) {
|
||||||
if (long fsize = ftell(file); startPos > fsize) {
|
if (long fsize = ftell(file); startPos > fsize) {
|
||||||
error("Specified start position is greater than length of file '%s'", name.c_str());
|
error("Specified start position is greater than length of file '%s'", name.c_str());
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
24
test/asm/readfile-binary.asm
Normal file
24
test/asm/readfile-binary.asm
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
section "tilemap", rom0
|
||||||
|
/*
|
||||||
|
input:
|
||||||
|
$20
|
||||||
|
$01 $03 $05 $07 $09
|
||||||
|
$02 $04 $06 $08 $10
|
||||||
|
$00 $de $01 $df $80
|
||||||
|
5
|
||||||
|
*/
|
||||||
|
def tilemap equs readfile("readfile-binary.inc.bin")
|
||||||
|
def area = bytelen(#tilemap) - 2
|
||||||
|
def offset = strbyte(#tilemap, 0)
|
||||||
|
def width = strbyte(#tilemap, area + 1)
|
||||||
|
db width, area / width
|
||||||
|
for idx, area
|
||||||
|
db strbyte(#tilemap, idx + 1) + offset
|
||||||
|
endr
|
||||||
|
/*
|
||||||
|
output:
|
||||||
|
5, 3
|
||||||
|
$21 $23 $25 $27 $29
|
||||||
|
$22 $24 $26 $28 $30
|
||||||
|
$20 $fe $21 $ff $a0
|
||||||
|
*/
|
||||||
BIN
test/asm/readfile-binary.inc.bin
Normal file
BIN
test/asm/readfile-binary.inc.bin
Normal file
Binary file not shown.
1
test/asm/readfile-binary.out.bin
Normal file
1
test/asm/readfile-binary.out.bin
Normal file
@@ -0,0 +1 @@
|
|||||||
|
!#%')"$&(0 <20>!<21><>
|
||||||
2
test/asm/readfile-nonexist.asm
Normal file
2
test/asm/readfile-nonexist.asm
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
def s equs readfile("readfile-nonexist.inc")
|
||||||
|
assert strlen(#s) == 0
|
||||||
3
test/asm/readfile-nonexist.err
Normal file
3
test/asm/readfile-nonexist.err
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
error: readfile-nonexist.asm(1):
|
||||||
|
Error opening READFILE file 'readfile-nonexist.inc': No such file or directory
|
||||||
|
Assembly aborted with 1 error!
|
||||||
5
test/asm/readfile.asm
Normal file
5
test/asm/readfile.asm
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
def s equs readfile("readfile.inc")
|
||||||
|
println strupr(#s) ++ "!"
|
||||||
|
|
||||||
|
redef s equs readfile("readfile.inc", 5)
|
||||||
|
println strrpl(#s, "l", "w") ++ "?"
|
||||||
1
test/asm/readfile.inc
Normal file
1
test/asm/readfile.inc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
hello world
|
||||||
2
test/asm/readfile.out
Normal file
2
test/asm/readfile.out
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
HELLO WORLD!
|
||||||
|
hewwo?
|
||||||
Reference in New Issue
Block a user