mirror of
https://github.com/hsoft/collapseos.git
synced 2024-11-27 12:08:07 +11:00
tools: add blkunpack
and remove cfspack, which will not ever be used again.
This commit is contained in:
parent
5cb4a7de9a
commit
79ce88c12c
12
blk/001
12
blk/001
@ -2,3 +2,15 @@ MASTER INDEX
|
|||||||
|
|
||||||
3 Usage 30 Dictionary
|
3 Usage 30 Dictionary
|
||||||
70 Implementation notes 100 Block editor
|
70 Implementation notes 100 Block editor
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
4
blk/003
4
blk/003
@ -10,3 +10,7 @@ Contents
|
|||||||
4 DOES> 6 Compilation vs meta-comp.
|
4 DOES> 6 Compilation vs meta-comp.
|
||||||
8 I/O 11 Chained comparisons
|
8 I/O 11 Chained comparisons
|
||||||
14 Addressed devices 18 Signed-ness
|
14 Addressed devices 18 Signed-ness
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
1
blk/004
1
blk/004
@ -13,3 +13,4 @@ At compile time, colon definition stops processing words when
|
|||||||
reaching the DOES>.
|
reaching the DOES>.
|
||||||
|
|
||||||
Example: ": CONSTANT CREATE HERE @ ! DOES> @ ;"
|
Example: ": CONSTANT CREATE HERE @ ! DOES> @ ;"
|
||||||
|
|
||||||
|
2
blk/009
2
blk/009
@ -12,3 +12,5 @@ new "(parse)" word.
|
|||||||
|
|
||||||
This way, we have a full-featured (and extensible) parsing with
|
This way, we have a full-featured (and extensible) parsing with
|
||||||
a tiny native core.
|
a tiny native core.
|
||||||
|
|
||||||
|
|
||||||
|
12
blk/016
12
blk/016
@ -2,3 +2,15 @@
|
|||||||
kind of ad-hoc way to have some kind of mapping function, but
|
kind of ad-hoc way to have some kind of mapping function, but
|
||||||
what you'll mostly want to to is to plug device drivers into
|
what you'll mostly want to to is to plug device drivers into
|
||||||
those words.
|
those words.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
7
blk/018
7
blk/018
@ -7,3 +7,10 @@ support the "-" prefix, but under the hood, it's all unsigned.
|
|||||||
This leads to some oddities. For example, "-1 0 <" is false.
|
This leads to some oddities. For example, "-1 0 <" is false.
|
||||||
To compare whether something is negative, use the "<0" word
|
To compare whether something is negative, use the "<0" word
|
||||||
which is the equivalent to "0x7fff >".
|
which is the equivalent to "0x7fff >".
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
5
blk/030
5
blk/030
@ -9,3 +9,8 @@ Be sure to read usage guide (B3) first.
|
|||||||
52 Addressed devices 54 Arithmetic / Bits
|
52 Addressed devices 54 Arithmetic / Bits
|
||||||
56 Logic 58 Strings
|
56 Logic 58 Strings
|
||||||
60 I/O 64 Disk
|
60 I/O 64 Disk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
6
blk/032
6
blk/032
@ -8,3 +8,9 @@ Words between "()" are "support words" that aren't really meant
|
|||||||
to be used directly, but as part of another word.
|
to be used directly, but as part of another word.
|
||||||
|
|
||||||
"*I*" in description indicates an IMMEDIATE word.
|
"*I*" in description indicates an IMMEDIATE word.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
13
blk/035
13
blk/035
@ -1,3 +1,16 @@
|
|||||||
(cont.)
|
(cont.)
|
||||||
~ - Container for native code. Usually not an executable word.
|
~ - Container for native code. Usually not an executable word.
|
||||||
? - Is it ...? (example: IMMED?)
|
? - Is it ...? (example: IMMED?)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
10
blk/038
10
blk/038
@ -4,3 +4,13 @@ FORGET x -- Rewind the dictionary (both CURRENT and HERE)
|
|||||||
PREV a -- a Return a wordref's previous entry.
|
PREV a -- a Return a wordref's previous entry.
|
||||||
WHLEN a -- n Get word header length from wordref. That is,
|
WHLEN a -- n Get word header length from wordref. That is,
|
||||||
name length + 3. a is a wordref
|
name length + 3. a is a wordref
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
1
blk/043
1
blk/043
@ -13,3 +13,4 @@ INTERPRET -- Get a line from stdin, compile it in tmp memory,
|
|||||||
then execute the compiled contents.
|
then execute the compiled contents.
|
||||||
QUIT -- Return to interpreter prompt immediately
|
QUIT -- Return to interpreter prompt immediately
|
||||||
EXIT! -- Exit current INTERPRET loop.
|
EXIT! -- Exit current INTERPRET loop.
|
||||||
|
|
||||||
|
5
blk/046
5
blk/046
@ -9,3 +9,8 @@ SWAP a b -- b a
|
|||||||
2DUP a b -- a b a b
|
2DUP a b -- a b a b
|
||||||
2OVER a b c d -- a b c d a b
|
2OVER a b c d -- a b c d a b
|
||||||
2SWAP a b c d -- c d a b
|
2SWAP a b c d -- c d a b
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
9
blk/048
9
blk/048
@ -5,3 +5,12 @@ R> R:n -- n Pops RS and push to PS
|
|||||||
I -- n Copy RS TOS to PS
|
I -- n Copy RS TOS to PS
|
||||||
I' -- n Copy RS second item to PS
|
I' -- n Copy RS second item to PS
|
||||||
J -- n Copy RS third item to PS
|
J -- n Copy RS third item to PS
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
6
blk/052
6
blk/052
@ -8,3 +8,9 @@ A! c a -- Indirect C!
|
|||||||
A@* -- a Address for A@ word
|
A@* -- a Address for A@ word
|
||||||
A!* -- a Address for A! word
|
A!* -- a Address for A! word
|
||||||
AMOVE src dst u -- Same as MOVE, but with A@ and A!
|
AMOVE src dst u -- Same as MOVE, but with A@ and A!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
2
blk/054
2
blk/054
@ -12,3 +12,5 @@ OR a b -- c a | b -> c
|
|||||||
XOR a b -- c a ^ b -> c
|
XOR a b -- c a ^ b -> c
|
||||||
|
|
||||||
Shortcuts: 1+ 2+ 1- 2-
|
Shortcuts: 1+ 2+ 1- 2-
|
||||||
|
|
||||||
|
|
||||||
|
8
blk/056
8
blk/056
@ -6,3 +6,11 @@ Logic
|
|||||||
CMP n1 n2 -- n Compare n1 and n2 and set n to -1, 0, or 1.
|
CMP n1 n2 -- n Compare n1 and n2 and set n to -1, 0, or 1.
|
||||||
n=0: a1=a2. n=1: a1>a2. n=-1: a1<a2.
|
n=0: a1=a2. n=1: a1>a2. n=-1: a1<a2.
|
||||||
NOT f -- f Push the logical opposite of f
|
NOT f -- f Push the logical opposite of f
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
6
blk/058
6
blk/058
@ -8,3 +8,9 @@ LITS a -- Write word at addr a as a atring literal.
|
|||||||
S= a1 a2 -- f Returns whether string a1 == a2.
|
S= a1 a2 -- f Returns whether string a1 == a2.
|
||||||
SCPY a -- Copy string at addr a into HERE.
|
SCPY a -- Copy string at addr a into HERE.
|
||||||
SLEN a -- n Push length of str at a.
|
SLEN a -- n Push length of str at a.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
10
blk/064
10
blk/064
@ -4,3 +4,13 @@ BLK> -- a Address of the current block variable.
|
|||||||
LIST n -- Prints the contents of the block n on screen in the
|
LIST n -- Prints the contents of the block n on screen in the
|
||||||
form of 16 lines of 64 columns.
|
form of 16 lines of 64 columns.
|
||||||
LOAD n -- Interprets Forth code from block n
|
LOAD n -- Interprets Forth code from block n
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
10
blk/070
10
blk/070
@ -4,3 +4,13 @@ Implementation notes
|
|||||||
75 Stack management 77 Dictionary
|
75 Stack management 77 Dictionary
|
||||||
80 System variables 85 Word routines
|
80 System variables 85 Word routines
|
||||||
89 Initialization sequence
|
89 Initialization sequence
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
5
blk/071
5
blk/071
@ -9,3 +9,8 @@ it. As a general rule, we go like this:
|
|||||||
4. Is it a number?
|
4. Is it a number?
|
||||||
5. If yes, push that number to PS, goto 1
|
5. If yes, push that number to PS, goto 1
|
||||||
6. Error: undefined word.
|
6. Error: undefined word.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
2
blk/075
2
blk/075
@ -12,3 +12,5 @@ IX always points to RS' Top Of Stack (TOS)
|
|||||||
This return stack contain "Interpreter pointers", that is a
|
This return stack contain "Interpreter pointers", that is a
|
||||||
pointer to the address of a word, as seen in a compiled list of
|
pointer to the address of a word, as seen in a compiled list of
|
||||||
words.
|
words.
|
||||||
|
|
||||||
|
|
||||||
|
4
blk/078
4
blk/078
@ -10,3 +10,7 @@ chain). There are also "special words", for example NUMBER,
|
|||||||
LIT, FBR, that have a slightly different structure. They're
|
LIT, FBR, that have a slightly different structure. They're
|
||||||
also a pointer to an executable, but as for the other fields,
|
also a pointer to an executable, but as for the other fields,
|
||||||
the only one they have is the "flags" field.
|
the only one they have is the "flags" field.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
10
blk/084
10
blk/084
@ -4,3 +4,13 @@ DRIVERS section is reserved for recipe-specific
|
|||||||
drivers. Here is a list of known usages:
|
drivers. Here is a list of known usages:
|
||||||
|
|
||||||
* 0x70-0x78: ACIA buffer pointers in RC2014 recipes.
|
* 0x70-0x78: ACIA buffer pointers in RC2014 recipes.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
10
blk/087
10
blk/087
@ -4,3 +4,13 @@ being followed by a 2 byte number, it is followed by a
|
|||||||
null-terminated string. Upon execution, the address of that
|
null-terminated string. Upon execution, the address of that
|
||||||
null-terminated string is pushed on the PSP and IP is advanced
|
null-terminated string is pushed on the PSP and IP is advanced
|
||||||
to the address following the null.
|
to the address following the null.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
9
blk/091
9
blk/091
@ -5,3 +5,12 @@ for space with HERE: New entries to the dict will overwrite
|
|||||||
that code! Also, because we're barebone, we can't have
|
that code! Also, because we're barebone, we can't have
|
||||||
comments. This can lead to peculiar code in this area where we
|
comments. This can lead to peculiar code in this area where we
|
||||||
try to "waste" space in initialization code.
|
try to "waste" space in initialization code.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
14
blk/101
14
blk/101
@ -1,2 +1,16 @@
|
|||||||
T ( n -- ): select line n for editing.
|
T ( n -- ): select line n for editing.
|
||||||
P xxx(return): put typed line on selected line.
|
P xxx(return): put typed line on selected line.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
6
blk/103
6
blk/103
@ -8,3 +8,9 @@ VARIABLE ACC
|
|||||||
: L BLK> @ _LIST ;
|
: L BLK> @ _LIST ;
|
||||||
: B BLK> @ 1- BLK> ! L ;
|
: B BLK> @ 1- BLK> ! L ;
|
||||||
: N BLK> @ 1+ BLK> ! L ;
|
: N BLK> @ 1+ BLK> ! L ;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
3
blk/104
3
blk/104
@ -11,3 +11,6 @@ VARIABLE EDPOS
|
|||||||
DUP IF DROP C< THEN
|
DUP IF DROP C< THEN
|
||||||
LOOP
|
LOOP
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
1
tools/.gitignore
vendored
1
tools/.gitignore
vendored
@ -9,3 +9,4 @@
|
|||||||
/bin2c
|
/bin2c
|
||||||
/exec
|
/exec
|
||||||
/blkpack
|
/blkpack
|
||||||
|
/blkunpack
|
||||||
|
@ -9,9 +9,10 @@ STRIPFC_TGT = stripfc
|
|||||||
BIN2C_TGT = bin2c
|
BIN2C_TGT = bin2c
|
||||||
EXEC_TGT = exec
|
EXEC_TGT = exec
|
||||||
BLKPACK_TGT = blkpack
|
BLKPACK_TGT = blkpack
|
||||||
|
BLKUNPACK_TGT = blkunpack
|
||||||
TARGETS = $(MEMDUMP_TGT) $(BLKDUMP_TGT) $(UPLOAD_TGT) $(FONTCOMPILE_TGT) \
|
TARGETS = $(MEMDUMP_TGT) $(BLKDUMP_TGT) $(UPLOAD_TGT) $(FONTCOMPILE_TGT) \
|
||||||
$(TTYSAFE_TGT) $(PINGPONG_TGT) $(SLATEST_TGT) $(STRIPFC_TGT) \
|
$(TTYSAFE_TGT) $(PINGPONG_TGT) $(SLATEST_TGT) $(STRIPFC_TGT) \
|
||||||
$(BIN2C_TGT) $(EXEC_TGT) $(BLKPACK_TGT)
|
$(BIN2C_TGT) $(EXEC_TGT) $(BLKPACK_TGT) $(BLKUNPACK_TGT)
|
||||||
OBJS = common.o
|
OBJS = common.o
|
||||||
|
|
||||||
all: $(TARGETS)
|
all: $(TARGETS)
|
||||||
@ -31,6 +32,7 @@ $(STRIPFC_TGT): $(STRIPFC_TGT).c
|
|||||||
$(BIN2C_TGT): $(BIN2C_TGT).c
|
$(BIN2C_TGT): $(BIN2C_TGT).c
|
||||||
$(EXEC_TGT): $(EXEC_TGT).c
|
$(EXEC_TGT): $(EXEC_TGT).c
|
||||||
$(BLKPACK_TGT): $(BLKPACK_TGT).c
|
$(BLKPACK_TGT): $(BLKPACK_TGT).c
|
||||||
|
$(BLKUNPACK_TGT): $(BLKUNPACK_TGT).c
|
||||||
$(TARGETS): $(OBJS)
|
$(TARGETS): $(OBJS)
|
||||||
$(CC) $(CFLAGS) $@.c $(OBJS) -o $@
|
$(CC) $(CFLAGS) $@.c $(OBJS) -o $@
|
||||||
|
|
||||||
|
44
tools/blkunpack.c
Normal file
44
tools/blkunpack.c
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
void usage()
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Usage: blkunpack dirname\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
char buf[1024];
|
||||||
|
int blkid = 0;
|
||||||
|
if (argc != 2) {
|
||||||
|
usage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
while (fread(buf, 1024, 1, stdin) == 1) {
|
||||||
|
char fullpath[0x200];
|
||||||
|
sprintf(fullpath, "%s/%03d", argv[1], blkid);
|
||||||
|
char c = 0;
|
||||||
|
for (int i=0; i<1024; i++) {
|
||||||
|
c |= buf[i];
|
||||||
|
}
|
||||||
|
if (c) {
|
||||||
|
// not an empty block
|
||||||
|
printf("%s\n", fullpath);
|
||||||
|
FILE *fp = fopen(fullpath, "w");
|
||||||
|
for (int i=0; i<16; i++) {
|
||||||
|
int len = strlen(&buf[i*64]);
|
||||||
|
fwrite(&buf[i*64], len, 1, fp);
|
||||||
|
fputc('\n', fp);
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
} else {
|
||||||
|
// empty block, delete
|
||||||
|
unlink(fullpath);
|
||||||
|
}
|
||||||
|
blkid++;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
2
tools/cfspack/.gitignore
vendored
2
tools/cfspack/.gitignore
vendored
@ -1,2 +0,0 @@
|
|||||||
/cfspack
|
|
||||||
/cfsunpack
|
|
@ -1,16 +0,0 @@
|
|||||||
TARGETS = cfspack cfsunpack
|
|
||||||
|
|
||||||
CFSPACK_OBJS = cfspack.o libcfs.o
|
|
||||||
|
|
||||||
.PHONY: all
|
|
||||||
all: $(TARGETS)
|
|
||||||
|
|
||||||
cfspack: $(CFSPACK_OBJS)
|
|
||||||
$(CC) $(LDFLAGS) -o $@ $(CFSPACK_OBJS)
|
|
||||||
|
|
||||||
cfsunpack: cfsunpack.c
|
|
||||||
$(CC) $(CFLAGS) -o $@ cfsunpack.c
|
|
||||||
|
|
||||||
.PHONY: clean
|
|
||||||
clean:
|
|
||||||
rm -f $(TARGETS)
|
|
@ -1,33 +0,0 @@
|
|||||||
# cfspack
|
|
||||||
|
|
||||||
A tool/library to pack files into a CFS blob and unpack a CFS blob into
|
|
||||||
a directory.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
To pack a directory into a CFS blob, run:
|
|
||||||
|
|
||||||
cfspack /path/to/directory
|
|
||||||
|
|
||||||
The blob is spit to stdout. If there are subdirectories, they will be prefixes
|
|
||||||
to the filenames under it.
|
|
||||||
|
|
||||||
`cfspack` takes optional -p pattern arguments. If specified, only files
|
|
||||||
matching at least one of the patterns ("fnmatch" style") will be included.
|
|
||||||
|
|
||||||
If path is a file, a CFS with a single file will be spit and its name will
|
|
||||||
exclude the directory part of that filename.
|
|
||||||
|
|
||||||
The chain being spitted is always ended with a "stop block" (a zero-allocation
|
|
||||||
block that stops the CFS chain). You can call `cfspack` with no argument to get
|
|
||||||
only a stop block.
|
|
||||||
|
|
||||||
The program errors out if a file name is too long (> 26 bytes) or too big
|
|
||||||
(> 0x10000 - 0x20 bytes).
|
|
||||||
|
|
||||||
To unpack a blob to a directory:
|
|
||||||
|
|
||||||
cfsunpack /path/to/dest < blob
|
|
||||||
|
|
||||||
If destination exists, files are created alongside existing ones. If a file to
|
|
||||||
unpack already exists, it is overwritten.
|
|
@ -1,10 +0,0 @@
|
|||||||
#define BLKSIZE 0x100
|
|
||||||
#define HEADERSIZE 0x20
|
|
||||||
#define MAX_FN_LEN 25 // 26 - null char
|
|
||||||
#define MAX_FILE_SIZE (BLKSIZE * 0x100) - HEADERSIZE
|
|
||||||
|
|
||||||
void set_spit_stream(FILE *stream);
|
|
||||||
int is_regular_file(char *path);
|
|
||||||
void spitempty();
|
|
||||||
int spitblock(char *fullpath, char *fn);
|
|
||||||
int spitdir(char *path, char *prefix, char **patterns);
|
|
@ -1,54 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <fnmatch.h>
|
|
||||||
#include <libgen.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#include "cfs.h"
|
|
||||||
|
|
||||||
void usage()
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Usage: cfspack [-p pattern] [/path/to/dir...]\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
int patterncount = 0;
|
|
||||||
char **patterns = malloc(sizeof(char**));
|
|
||||||
patterns[0] = NULL;
|
|
||||||
while (1) {
|
|
||||||
int c = getopt(argc, argv, "p:");
|
|
||||||
if (c < 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
switch (c) {
|
|
||||||
case 'p':
|
|
||||||
patterns[patterncount] = optarg;
|
|
||||||
patterncount++;
|
|
||||||
patterns = realloc(patterns, sizeof(char**)*(patterncount+1));
|
|
||||||
patterns[patterncount] = NULL;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
usage();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int res = 0;
|
|
||||||
for (int i=optind; i<argc; i++) {
|
|
||||||
if (is_regular_file(argv[i])) {
|
|
||||||
// special case: just one file
|
|
||||||
res = spitblock(argv[i], basename(argv[i]));
|
|
||||||
} else {
|
|
||||||
res = spitdir(argv[i], "", patterns);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (res == 0) {
|
|
||||||
spitempty();
|
|
||||||
}
|
|
||||||
free(patterns);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
@ -1,93 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#define BLKSIZE 0x100
|
|
||||||
#define HEADERSIZE 0x20
|
|
||||||
#define MAX_FN_LEN 25 // 26 - null char
|
|
||||||
|
|
||||||
bool ensuredir(char *path)
|
|
||||||
{
|
|
||||||
char *s = path;
|
|
||||||
while (*s != '\0') {
|
|
||||||
if (*s == '/') {
|
|
||||||
*s = '\0';
|
|
||||||
struct stat path_stat;
|
|
||||||
if (stat(path, &path_stat) != 0) {
|
|
||||||
if (mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*s = '/';
|
|
||||||
}
|
|
||||||
s++;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool unpackblk(char *dstpath)
|
|
||||||
{
|
|
||||||
char buf[MAX_FN_LEN+1];
|
|
||||||
if (fgets(buf, 3+1, stdin) == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (strcmp(buf, "CFS") != 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
int c = getchar();
|
|
||||||
uint8_t blkcnt = c;
|
|
||||||
if (blkcnt == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
c = getchar();
|
|
||||||
uint16_t fsize = c & 0xff;
|
|
||||||
c = getchar();
|
|
||||||
fsize |= (c & 0xff) << 8;
|
|
||||||
|
|
||||||
if (fgets(buf, MAX_FN_LEN+1+1, stdin) == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
char fullpath[0x1000];
|
|
||||||
strcpy(fullpath, dstpath);
|
|
||||||
strcat(fullpath, "/");
|
|
||||||
strcat(fullpath, buf);
|
|
||||||
if (!ensuredir(fullpath)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
int blksize = (BLKSIZE-HEADERSIZE)+(BLKSIZE*(blkcnt-1));
|
|
||||||
int skipcount = blksize - fsize;
|
|
||||||
FILE *fp = fopen(fullpath, "w");
|
|
||||||
while (fsize) {
|
|
||||||
c = getchar();
|
|
||||||
if (c == EOF) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
fputc(c, fp);
|
|
||||||
fsize--;
|
|
||||||
}
|
|
||||||
fclose(fp);
|
|
||||||
while (skipcount) {
|
|
||||||
getchar();
|
|
||||||
skipcount--;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
if (argc != 2) {
|
|
||||||
fprintf(stderr, "Usage: cfspack /path/to/dest\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
char *dstpath = argv[1];
|
|
||||||
// we fail if there isn't at least one block
|
|
||||||
if (!unpackblk(dstpath)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
while (unpackblk(dstpath));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,152 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <fnmatch.h>
|
|
||||||
#include <libgen.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#include "cfs.h"
|
|
||||||
|
|
||||||
#define PUTC(c) putc(c, spitstream)
|
|
||||||
|
|
||||||
static FILE *spitstream = NULL;
|
|
||||||
|
|
||||||
void set_spit_stream(FILE *stream)
|
|
||||||
{
|
|
||||||
spitstream = stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
int is_regular_file(char *path)
|
|
||||||
{
|
|
||||||
struct stat path_stat;
|
|
||||||
stat(path, &path_stat);
|
|
||||||
return S_ISREG(path_stat.st_mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
void spitempty()
|
|
||||||
{
|
|
||||||
if (spitstream == NULL) spitstream = stdout;
|
|
||||||
PUTC('C');
|
|
||||||
PUTC('F');
|
|
||||||
PUTC('S');
|
|
||||||
for (int i=0; i<0x20-3; i++) {
|
|
||||||
PUTC(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int spitblock(char *fullpath, char *fn)
|
|
||||||
{
|
|
||||||
if (spitstream == NULL) spitstream = stdout;
|
|
||||||
FILE *fp = fopen(fullpath, "r");
|
|
||||||
fseek(fp, 0, SEEK_END);
|
|
||||||
long fsize = ftell(fp);
|
|
||||||
if (fsize > MAX_FILE_SIZE) {
|
|
||||||
fclose(fp);
|
|
||||||
fprintf(stderr, "File too big: %s %ld\n", fullpath, fsize);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
/* Compute block count.
|
|
||||||
* We always have at least one, which contains 0x100 bytes - 0x20, which is
|
|
||||||
* metadata. The rest of the blocks have a steady 0x100.
|
|
||||||
*/
|
|
||||||
unsigned char blockcount = 1;
|
|
||||||
int fsize2 = fsize - (BLKSIZE - HEADERSIZE);
|
|
||||||
if (fsize2 > 0) {
|
|
||||||
blockcount += (fsize2 / BLKSIZE);
|
|
||||||
}
|
|
||||||
if (blockcount * BLKSIZE < fsize + HEADERSIZE) {
|
|
||||||
blockcount++;
|
|
||||||
}
|
|
||||||
PUTC('C');
|
|
||||||
PUTC('F');
|
|
||||||
PUTC('S');
|
|
||||||
PUTC(blockcount);
|
|
||||||
// file size is little endian
|
|
||||||
PUTC(fsize & 0xff);
|
|
||||||
PUTC((fsize >> 8) & 0xff);
|
|
||||||
int fnlen = strlen(fn);
|
|
||||||
for (int i=0; i<MAX_FN_LEN; i++) {
|
|
||||||
if (i < fnlen) {
|
|
||||||
PUTC(fn[i]);
|
|
||||||
} else {
|
|
||||||
PUTC(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// And the last FN char which is always null
|
|
||||||
PUTC(0);
|
|
||||||
char buf[MAX_FILE_SIZE] = {0};
|
|
||||||
rewind(fp);
|
|
||||||
fread(buf, fsize, 1, fp);
|
|
||||||
fclose(fp);
|
|
||||||
fwrite(buf, (blockcount * BLKSIZE) - HEADERSIZE, 1, spitstream);
|
|
||||||
fflush(spitstream);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int spitdir(char *path, char *prefix, char **patterns)
|
|
||||||
{
|
|
||||||
DIR *dp;
|
|
||||||
struct dirent *ep;
|
|
||||||
|
|
||||||
int prefixlen = strlen(prefix);
|
|
||||||
dp = opendir(path);
|
|
||||||
if (dp == NULL) {
|
|
||||||
fprintf(stderr, "Couldn't open directory.\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
while ((ep = readdir(dp))) {
|
|
||||||
if ((strcmp(ep->d_name, ".") == 0) || strcmp(ep->d_name, "..") == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (ep->d_type != DT_DIR && ep->d_type != DT_REG) {
|
|
||||||
fprintf(stderr, "Only regular file or directories are supported\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
int slen = strlen(ep->d_name);
|
|
||||||
if (prefixlen + slen> MAX_FN_LEN) {
|
|
||||||
fprintf(stderr, "Filename too long: %s/%s\n", prefix, ep->d_name);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
char fullpath[0x1000];
|
|
||||||
strcpy(fullpath, path);
|
|
||||||
strcat(fullpath, "/");
|
|
||||||
strcat(fullpath, ep->d_name);
|
|
||||||
char newprefix[MAX_FN_LEN];
|
|
||||||
strcpy(newprefix, prefix);
|
|
||||||
if (prefixlen > 0) {
|
|
||||||
strcat(newprefix, "/");
|
|
||||||
}
|
|
||||||
strcat(newprefix, ep->d_name);
|
|
||||||
if (ep->d_type == DT_DIR) {
|
|
||||||
int r = spitdir(fullpath, newprefix, patterns);
|
|
||||||
if (r != 0) {
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
char **p = patterns;
|
|
||||||
// if we have no pattern, we match all
|
|
||||||
if (p && *p) {
|
|
||||||
int matches = 0;
|
|
||||||
while (*p) {
|
|
||||||
if (fnmatch(*p, ep->d_name, 0) == 0) {
|
|
||||||
matches = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
if (!matches) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int r = spitblock(fullpath, newprefix);
|
|
||||||
if (r != 0) {
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
closedir(dp);
|
|
||||||
return 0;
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user