diff --git a/src/iwannaflycurses.messy b/src/iwannaflycurses.messy index 16b1ad5..c071037 100644 --- a/src/iwannaflycurses.messy +++ b/src/iwannaflycurses.messy @@ -29,29 +29,23 @@ * other documentation, as well as give a deeper understanding of the * program because, ya'know, source code. * - * note to imaginary future packagers: if packaged in binary form, a dummy header might - * still be required to support the obfuscated C syntax that is recommended for use - * with this library. if you don't know what "obfuscated" C is, look at the original - * unix v7 source of ye old bourne shell for the definative example. + * The engine provides the C libraries and Perl modules needed to write a game. * - * the engine should be compiled as a shared object, and installed according to the - * values set in paths.h - * - * a game compiled for the engine should obey the following conventions: - * - it should provide the bulk of it's functions in a shared library in a private - * application directory - * - it should provide special use functions, such as events, as seperate shared objects, - * to be dynamically loaded at runtime. - * - it should provide special use constants likewise - * blocks of text, however, should be regular nfo or txt - * - it should store any dynamic constants in the savefile, as r, hex, tsv, csv, ini, txt, nfo - * - it should provide at least the following applications (in $PATH) : - * - the game itself - * - a seperate program for generating a new savefile - * - should use (int argc, char *argv[]) to provide bypassing of dotfile, - * path to a non-default dotfile, overrides of race, class, alignment prefs, - * and other flags as desired. parsed by a different parser from the dotfile. - * - a savefile editor + * a game using the engine should provide the following: + * - A newgame initializer perl script + * - Any Guile extensions and shared resources needed by events + * the resource files MUST follow the following naming conventions: + * - .hex or .hex## for packed unsigned byte arrays, where ## is a power of 8 + * - .bin or .bin## for packed signed byte arrays, where ## is a power of 8 + * - .dat for the custom data format + * - .tsv or .tab for tab seperated values + * - .csv for comma seperated values + * - .asc for a plaintext line-seperated record + * - .txt for a plaintext block of text + * - .ans for an ansi escaped line-seperated record + * - .nfo for an ansi escaped block of text + * - Guile extensions to generate rooms + * - Shared resources used by events * * enviromental requirements: * - UTF-8 terminal of at least 24*80 characters @@ -1804,48 +1798,51 @@ enttype *ent_ptr mapobjtyp *obj_ptr lightyp *lamp_ptr -/* .r8 : binary data that is organized into 8 bit segments - * .r16 : binary data that is organized into 16 bit segments - * .r32 : binary data that is...you get the picture, right? - * .hex : binary data that is unorganized - * .csv : UTF-8 text record deliminated with commas and line breaks - * .tsv : UTF-8 text record deliminated with tabs and line breaks - * .ini : UTF-8 text record of key=value pairs deliminated with line breaks - * .so : a shared object. a game may use as many of these as it wants. Placing single-use functions in LD_LOAD_PATH is DISCOURAGED; they should be placed in the game's private files. - * .dat : internal data from inside a savefile - * .txt : UTF-8 text document. usually stored in the program's static files, which is CAT-ed to provide dialouge; also used in dumps of primatives - * .nfo : UTF-8 text document (not CP437) containing SGR sequences. otherwise identical to txt. - * .tar.gz : each room is saved as a tar.gz file, as are linked lists and the player - * .tar.bz2 : each plane is saved as a tar.bz2 file. - * .sav : a save file. formatted as a .tar.bz2 file tree - * .man.# : a linux manpage - * .mdoc.# : a bsd manpage - * : documentation - * .html : documentation - * .texinfo : documentation - * .d : everything relating to a struct is stored in the same directory, and pointers to structs are stored as subdirectories with the name of that field. the tail element in a linked list has no subdirectory named "next"; "prev" pointers are "../" implicitly. - * .*rc : a configuration file, parsed with the runcommander - * .dump : a debugger data dump, with a format somwhere between JSON, INI, and C-like pseudocode. essentialy a dat file in readable (and non-raw) format. +/* *.bin : signed binary data that is organized into 8 bit segments + * *.bin# : signed binary data that is organized into n bit segments + * *.hex : unsigned binary data that is organized into 8 bit segments + * *.hex# : unsigned binary data that is organized into n bit segments + * *.csv : UTF-8 text record deliminated with commas and line breaks + * *.tsv : UTF-8 text record deliminated with tabs and line breaks + * *.tab : same as tsv + * *.so : a shared object. a game may use as many of these as it wants. Placing single-use functions in LD_LOAD_PATH is DISCOURAGED; they should be placed in the game's private files. + * *.dat : internal data from inside a savefile + * *.json : a less compact container format that does not allow linking + * *.asc : UTF-8 text record delimenated by newline. + * *.txt : UTF-8 text document. usually stored in the program's static files, which is CAT-ed to provide dialouge; also used in dumps of primatives + * *.ans : UTF-8 text record containing SGR sequences deleminated by newline. + * *.nfo : UTF-8 text document (not CP437) containing SGR sequences. otherwise identical to asc. + * *.tar.gz : each room is saved as a tar.gz file, as are linked lists and the player + * *.tar.bz2 : each plane is saved as a tar.bz2 file. + * *.sav : a save file. formatted as a .tar.bz2 file tree + * *.man.# : a linux manpage + * *.mdoc.# : a bsd manpage + * * : documentation + * *.html : documentation + * *.texinfo : documentation + * *.pod : perl documentation + * *.d : everything relating to a struct is stored in the same directory, and pointers to structs are stored as subdirectories with the name of that field. the tail element in a linked list has no subdirectory named "next"; "prev" pointers are "../" implicitly. + * *.conf : a configuration file, parsed with perl. + * *.*rc : a user's configuration file. + * *.dump : a debugger data dump, with a format somwhere between JSON, INI, and C-like pseudocode. essentialy a dat file in readable (and non-raw) format. * * if the following new extensions collide with anything, I will change them. I specifically chose ones that were not a TLA or EFLA to try and avoid this, because there have been TDMTLA since before I came along. * .midibas : midiBASIC, to be parsed by the midi generator into a .h file. Represents a different subset of general midi than regular midi files, but does so in a human readable format. */ /* dat file format: - * BOM ESC ! @ IWannaFLyCurses DataFile NUL = Magic Cookie (no spaces) - * SOH name STX string ETX SEPERATOR = a text field - * SOH name DLE 16bit_length raw_data SEPERATOR = a numeric field - * SOH name ACK SEPERATOR = a boolean field that is true - * SOH name NAK SEPERATOR = a boolean field that is false - * SOH name XON path XOFF SEPERATOR = a pointer to the data at path - * SOH name SUB 8bit ... = array dimension - * an array will consist of either multiple bodys chained together without SEPERATOR or - * SOH name inbetween, or a single pointer to a r# or tsv file - * SOH name SOH name ... = a struct field - * EM = end of data + * SOH name BEL = struct field key + * STX string ETX = an ascii text field + * DLE s8bit raw_data = a numeric field, abs(n) bits long. a negative number indicates the field is signed. + * ACK = true + * NAK = false + * NUL = empty string + * XON path XOFF = a pointer to the data at path * - * seperator may be one of FS, GS, RS, or US; - * lower levels are used to indicate a suboordinate struct + * SYN = start an array or add a diminsion to the array + * structs are terminated with ETB + * arrays may have up to 4 dimensions seperated with FS GS RS and US. + * arrays are ended with EM */ struct mapgen_bordertyp { diff --git a/src/modules/IWannaFly/Macro.pm b/src/modules/IWannaFly/Macro.pm index dc89550..627d4fe 100644 --- a/src/modules/IWannaFly/Macro.pm +++ b/src/modules/IWannaFly/Macro.pm @@ -24,10 +24,6 @@ FILTER_ONLY code => sub { s/NIL/''/g }; FILTER_ONLY code => sub { s/¤/\$/g }; -FILTER_ONLY - code => sub { s/°/\$/g }; -FILTER_ONLY - code => sub { s/¢/\$/g }; FILTER_ONLY code => sub { s/£/\$/g }; FILTER_ONLY @@ -35,9 +31,7 @@ FILTER_ONLY FILTER_ONLY code => sub { s/§/\$/g }; FILTER_ONLY - code => sub { s/♯/\$/g }; -FILTER_ONLY - code => sub { s/‽(\()?/refaddr$1 /g }; + code => sub { s/¶/\$/g }; FILTER_ONLY code => sub { s/OK(AY)?/0/g }; FILTER_ONLY @@ -45,8 +39,24 @@ FILTER_ONLY FILTER_ONLY code => sub { s/ERR/-1/ig }; FILTER_ONLY - code => sub { s/«((?!»).*)»[\t]+:\(([\w]+)\)$/$1;\n\tgoto $2;\n/g }; + code => sub { s/«((?!»).*)»[\W]*:\(([\w]+)\)$/$1;\n\tgoto $2;\n/g }; +FILTER_ONLY + code => sub { s/«((?!»).*)»[\W]*:\{((?!\})\}$/$1;\n\t{$2};\n/g }; +FILTER_ONLY + code => sub { s/«((?!»).*)»[\W]*:S\{((?!\}).*)\}\n$/if ( $1 ) {\n\t$2;\n\t}/g }; +FILTER_ONLY + code => sub { s/«((?!»).*)»[\W]*:S\(([\w]+)\)\n$/if ( $1 ) {\n\tgoto $2;\n\t}/g }; +FILTER_ONLY + code => sub { s/«((?!»).*)»[\W]*:S\{((?!\}).*)\}\n$/unless ( $1 ) {\n\t$2;\n\t}/g }; +FILTER_ONLY + code => sub { s/«((?!»).*)»[\W]*:F\(([\w]+)\)\n$/unless ( $1 ) {\n\tgoto $2;\n\t}/g }; +FILTER_ONLY + code => sub { s/«((?!»).*)»[\W]*:S\{((?!\}).*)\}F\{((?!\}))\}\n$/if ( $1 ) {\n\t$2;\n\t}\nelse {\n\t$3;\n\t}/g }; +FILTER_ONLY + code => sub { s/«((?!»).*)»[\W]*:S\(((?!\)).*)\}F\{((?!\}))\}\n$/if ( $1 ) {\n\tgoto $2;\n\t}\nelse {\n\t$3;\n\t}/g }; +FILTER_ONLY + code => sub { s/«((?!»).*)»[\W]*:S\{((?!\}).*)\}F\(((?!\)))\}\n$/if ( $1 ) {\n\t$2;\n\t}\nelse {\n\tgoto $3;\n\t}/g }; FILTER_ONLY - code => sub { s/«((?!»).*)»[\t]+:S\(([\w]+)\)F\(([\w]+)\)\n$/if ( $1 ) {\n\tgoto $2;\n} else {\n\tgoto $3;\n}/g }; + code => sub { s/«((?!»).*)»[\W]*:S\(([\w]+)\)F\(([\w]+)\)\n$/if ( $1 ) {\n\tgoto $2;\n\t}\nelse {\n\tgoto $3;\n\t}/g }; FILTER_ONLY code => sub { s/: int;/: optimize(int);/g }; diff --git a/src/modules/IWannaFly/Main.pm b/src/modules/IWannaFly/Main.pm index a85781d..0f3535d 100644 --- a/src/modules/IWannaFly/Main.pm +++ b/src/modules/IWannaFly/Main.pm @@ -13,82 +13,58 @@ our @EXPORT; -# this program uses tight integration between C and Perl, -# using what I am terming "Black Magic" scalars -# (following the theme of the perl internal docs), -# as they are have the potential to aquire...ultimate power. -# -# the interaction these functions is Thus: -# -# C <+> Perl <+> C -# \________/ -# -# The XSUB is able to _leak_ newly created variables back to the -# grandparent C program by throwing them on the heap. in theory. -# It may instead anger the MMU, but there should be ways around this... -# (Thread safety? what's that?) -# I call this tunneling. -# -# These scalars will use special sigils: -# -# £foo is a complicated struct object ref -# ¢foo is a singly, circularly linked list -# €foo is a doubly, half-circularly linked list object -# §foo is a doubly, circularly linked list object -# -# any struct pointer fields are converted to hash objects. -# a Ptr field and a SYNC method will be added to these objects. -# -# Similarly, prev, next, or other pointer fields may be NULL -# (a Filter::Simple macro in IWannaFly'Null, which wrapps the C NULL), -# and ANY field may be undef or absent if it is to be untouched. -# circularly linked list iteration is terminated when -# $HEAD->Ptr == $CURR->next->Ptr -# all other linked list iteration is terminated when -# !$CURR->next -# -# more mundane sigils are also added: -# -# ¤bar is a reference -# °bar is a C pointer -# ♯bar is a bitfield or boolean -# ‽$bar is the refaddr function -# -# all these extra sigils except ‽ are processed to $ by -# Filter::Simple in IWannaFly::Macro. they mearly provide -# disambiguation of special scalars/fields while inspecting code. -# since THEY ARE ALL INTERCHANGEABLE WITH EACHOTHER AND $, their usage -# is akin to a footnote comment that referrs back here. -# when declaring a new ♯, you should use it with ": optimize(int);" -# from optimize::int, or the Filter::Simple macro for it in -# IWannaFly'Macro, which is simply ": int;" -# -# scalars generated from primatives will require a complementery pointer -# to be provided in order to be synced, while this is built in to the -# object resulting from a struct. -# -# modules for providing type support will be provided. -# every type will provide an assciated class. -# they will provide malloc, calloc, new, and fetch(°) -# these must be called by full name. -# -# ::malloc and ::calloc are wrappers for the C functions. -# they create a tunnelable heap object and pass the pointer -# -# ::new creates a blank object -# -# ::fetch(°ptr) creates a hash or scaler from a given °ptr -# -# once an object is created, a ->SYNC method will be created for it, -# which writes to ->Ptr -# -# fields which contain circularly linked lists may call IndexFwd and -# IndexBack on the reference itself to change the object pointed to. -# calling this on a half-circularly linked list will break it. -# calling IndexFwd on a Singly linked list creates a memory leak, -# calling IndexBack on a Singly linked list is a Bad Idea. -# -# free(♯Ptr) is untyped, and provided blessed. +# £foo is a singly linked list object +# ¢fie is a singly, circularly linked list object +# ♮bam is a doubly linked list +# €baz is a doubly, half-circularly linked list +# §qux is a doubly, circularly linked list +# ¤zot is a reference +# ¶xyz is a regex + +# Linked lists are provided using objects: +# ->Head is the first element; or an alias of ->Here for ¢ and § +# ->Tail is the last element, or an alias of ->Here->{prev} for §, or the ref whose ->{next} is equal to ->here for ¢ +# ->Offset($) element $-offset from ->Head +# ->Here is the currently indexed element +# ->FromHere($) element $-offset from ->Here + +# ->Adv advances ->Here by 1 +# ->FFwd($) advances ->Here by $ +# ->Back rewinds ->Here by 1 +# ->Rew($) rewinds ->Here by $ +# ->Reset resets ->Here to ->Head. +# ->Set($) set ->Here to ->Head + $. + +# ->Push adds a new element after ->Tail +# ->PushHere adds a new element at ->Here +# ->PushAt($) adds a new element at ->Offset($) +# ->PushTo($) adds a new element at ->FromHere($) + +# ->Pop removes ->Tail, or the node immidiately before ->Here for § +# ->PopHere removes ->Here +# ->PopAt($) removes ->Offset($) +# ->PopFrom($) removes ->FromHere($) + +# ->Punt sends ->Here to ->Tail (Do not pass go do not collect 200 scalars.) Alias of ->Adv for § +# ->PuntAt($) sends ->Offset($) to ->Tail +# ->PuntFrom($) sends ->Fromhere($) to ->Tail, or to before ->Here for § + +# The hashes in the objects are maintained as references. + +# classes for the objects are: +# IWannaFly::LinkedList::Single +# IWannaFly::LinkedList::SingleCircle +# IWannaFly::LinkedList::Double +# IWannaFly::LinkedList::DoubleHalf +# IWannaFly::LinkedList::DoubleCircle + +# each class supports: +# ::new makes an empty container with the methods above +# ::capture(\%) makes a container around a recursive hash, with members named +# ->{next} containing the recursion, as are created by the data file parsers. +# ->{prev} members may be overwritten, and if the formatting does not match, +# this could invoke the halting problem, i.e., capturing a circularly linked +# list with the non-circular class @@ -104,39 +80,3 @@ push(@EXPORT,'SGN'); sub INTVL($$$) {return MIN( MAX( $_[0],$_[1] ) ,$_[2] )} push(@EXPORT,'INTVL'); - diff --git a/src/modules/IWannaFly/Parse.pm b/src/modules/IWannaFly/Parse.pm new file mode 100644 index 0000000..3a983b3 --- /dev/null +++ b/src/modules/IWannaFly/Parse.pm @@ -0,0 +1,260 @@ +package IWannaFly::Parse; +use lib '..'; +use strict; +use warnings; +use feature "switch"; + +use IWannaFly::Struct; +use IWannaFly'Macro; +use IWannaFly'Null; +require Exporter; +our @ISA = qw(Exporter); +our @EXPORT; + +sub PARSEDAT($) { + open(my $file, <, shift); + my $nextchar = getc($file); + my %output; + my @stack = (\%output); + my $isarray = 0; + my $counter = 0; + sub storedata($) { + my $tmp = shift; + if ($isarray) { + push(@{¤stack[-1]},$tmp); + } + else { + ¤stack[-1] = $tmp; + } + sub append($) { + my $tmp = shift; + push(@stack,¤stack[-1]->{$tmp}); + } + while ($nextchar != "\004") + for ($buffer) { + + when (/^\0/) { + storedata(NIL); + } + + when (/^\001/) { + my $string = do { + local $/ = "\a"; + <$file> + } + chomp $string; + append($string); + } + + when (/^\002/) { + my $string = do { + local $/ = "\003"; + <$file> + } + chomp $string; + storedata($string); + } + + when (/^\006) { + store(true); + } + + when (/^\020) { + my $length = unpack('c',getc($file)) : int; + read($file,my $buffer,abs($length)); + if (abs($length) ≤ 8) { + if ($length > 0) { + my $output = unpack('C',$buffer); + } + else { + my $output = unpack('c',$buffer); + } + } + elseif (abs($length) ≤ 16) { + if ($length > 0) { + my $output = unpack('S',$buffer); + } + else { + my $output = unpack('s',$buffer); + } + } + elseif (abs($length) ≤ 32) { + if ($length > 0) { + my $output = unpack('L',$buffer); + } + else { + my $output = unpack('l',$buffer); + } + } + else { + if ($length > 0) { + my $output = unpack('Q',$buffer); + } + else { + my $output = unpack('q',$buffer); + } + } + storedata($output); + } + + when (/^\021/) { + my $path = do { + local $/ = "\023"; + <$file> + } + chomp $path; + storedata(DOTYPE($path,$islinkedlist,\%output)); + } + + when (/^\025/) { + storedata(false); + } + + when (/^\026/) { + if ($isarray) { + push(@{¤stack[-1]},[]); + push(@stack,¤stack[-1]); + } + else { + ¤stack[-1] = []; + } + $isarray++; + }; + + when (/^\027/) { + pop(@stack); + $counter++; + } + + when (/^\034/) { + if ($isarray) { + for (my $n = 1;$n < $isarray;$n++) { + pop(@stack); + } + for (my $n = 0;$n < $isarray;$n++) { + push(@{¤stack[-1]},[]); + push(@stack,¤stack[-1]); + } + } + } + + when (/^\035/) { + if ($isarray) { + for (my $n = 1;$n < MAX($isarray - 1,1);$n++) { + pop(@stack); + } + for (my $n = 0;$n < MAX($isarray - 1,1;$n++) { + push(@{¤stack[-1]},[]); + push(@stack,¤stack[-1]); + } + } + } + + when (/^\036/) { + if ($isarray) { + for (my $n = 1;$n < MAX($isarray - 2,1);$n++) { + pop(@stack); + } + for (my $n = 0;$n < MAX($isarray - 2,1;$n++) { + push(@{¤stack[-1]},[]); + push(@stack,¤stack[-1]); + } + } + } + + when (/^\037/) { + if ($isarray) { + for (my $n = 1;$n < MAX($isarray - 3,1);$n++) { + pop(@stack); + } + for (my $n = 0;$n < MAX($isarray - 3,1;$n++) { + push(@{¤stack[-1]},[]); + push(@stack,¤stack[-1]); + } + } + } + $nextchar = getc($file); + } + (\%output,¤prev); + } + +sub EATTYPE($$¤) { + my $path = shift; + my $islinkedlist = shift; + my ¤prev = shift; + $path =~ m/\.([^.]+)$/; + my $type = $1; + $path =~ m/(\d)+$/; + my $width = $1 ? $1 : 8; + my $output; + my ¤next; + for ($filetype) { + when (/^dat$/) { + (¤output,¤next) = PARSEDAT($path,$islinkedlist,¤prev); + when (/^(tsv|tab)$/) { + ¤output = READTAB($path); + } + when (/^hex[\d]*$/) { + ¤output = READHEX($path,$width); + } + when (/^bin[\d]*$/) { + ¤output = READBIN($path,$width); + } + when (/^(asc|ans)$/) { + ¤output = READRECORD($path); + } + when (/^txt|nfo$/) { + $output = READTEXT($path); + when (/^json$/) { + open(my $file,'<',$path); + $output = do { + local $\ = undef; + from_json(<$file>); + } + + } + } + ($output,¤next); + } + +sub READTEXT($) { + open(my $file,'<',shift); + local $\ = undef; + my $output = <$file>; + } + +sub READRECORD($) { + open(my $file,'<',shift); + my @output; + for ($file;!eof<$file>;push(@output,<$file>)); + \@output; + } + +sub READTAB($) { + open(my $file,'<',shift); + chomp(my @rows = <$file>); + my @table; + foreach (@rows) { + my @columns = split("\t",$_); + push(@table,\@columns); + } + \@table; + } + +sub READBIN($$) { + open(my $file,'<',shift); + my $size = shift; + local $\ = undef; + my $type = $size == 8 ? 'c' : $size == 16 ? 's' : $size == 32 ? 'l' : $size == 64 ? 'q' : false; + «$type» :F{die invalid filename} + unpack($type,<$file>); + } + +sub READHEX($$) + open(my $file,'<',shift); + my $size = shift; + local $\ = undef; + my $type = $size == 8 ? 'C' : $size == 16 ? 'S' : $size == 32 ? 'L' : $size == 64 ? 'Q' : false; + «$type» :F{die invalid filename} + unpack($type,<$file>); + }