VoteModule

For CastAVote, extracted from "VMSPEC1/4".

About VoteModule:

It was considered time for four things:
  1. I learnt a bit of C.
  2. CastAVote operated even faster.
  3. CastAVote used much less memory.
  4. CastAVote had a centralised resource.
Therefore this module was devised. It isn't in a finished state, but it should be in good enough order to work with CastAVote/NVP/VoteEdit or your own voting doors.

CastAVote no longer keeps track of the heap. Additionally, VoteEdit should use the facilities provided by VoteModule. For the more paranoid, there is a version of VoteEdit that manages the voted-on heap itself. Pick whichever you trust most. :-)

The eventual aim is to have this module look after all of CastAVote's file functions in ALL CastAVote software. You load this module up when you run the BBS - and any CastAVote utility (except VoteFile) will interface with the module instead of interfacing with the disc drive. As C is faster than BASIC, you will notice the whole system runs faster.

How many K!!!???

When initialised, this module grabs about 220K of RMA space in which to load the votes data. This size WILL NOT change in use.

Why 220K? Each string is 61 bytes long (60+zero). There are 15 strings in a vote. There are 220 votes. This is 201300 bytes, and not including results (about 8K more) or internal variables. Say 220K in all. The internal structure is exactly 1K in size, so 220K it is...

Now look at BASIC. BASIC reserves 256 bytes per string... Times 15 times 220. Right. 844800 bytes. Erm... Now each copy of CastAVote loaded would try to grab Nearly 1Mb (+code and internal variables).

The module provides for a mechanism to 'supply' data upon demand, and to reduce CastAVote to a shell that doesn't eat much memory. To add to that, loading two instantations of CastAVote will require much less memory than it used to.

There is a version of VoteModule (1.18c) that builds the vote data on disc (c is for cache). This will then supply data from disc instead of from RAM. It is noticeably slower but uses much less memory. I developed it because 220K was too much when loading C and the BBS software together. It is not an official release.

Later versions of VoteModule are expected to address this problem with the use of linked lists. This will allow VoteModule to claim memory as it needs is, rather than waste 200K on a vote data file that is only ten votes long.

Introduction

The VoteModule provides a simple and efficient way to interact with CastAVote's data files. It will keep track of the votes, results and soon it will take over heap management. Currently it provides all heap operations, but increment/decrement have not been fully tested.

Interrupts - VoteModule does not claim/alter interrupts.
Processor mode - SVC.
Re-entrancy - VoteModule is not re-entrant.

SWI Calls

VoteModule_LoadVotes                                      (SWI &16F00)

Load the votes data into memory.

   On entry
      R0 = 'unsaved data' switch override.
      R1 = Pointer to filename.
           No filename will default to "<CastAVoteA$Dir>.Data.Data".

   On exit
      R0 = Operation status:
            1 - File loaded okay.
            2 - Load failed because of unsaved data.
            3 - Load failed because file cannot be found or opened.
            4 - Load failed because data is corrupted (when reading string).
            5 - Load failed because data is corrupted (when reading word).

   Use:
      VoteModule_LoadVotes will load the votes data from the file specified,
      or the default file. This operation will usually fail if there is
      unsaved data. However you can pass a special number in R0 to override
      this. The special number is randomly chosen at start-up, and the method
      of obtaining this number is currently undocumented. Due to a glitch,
      you will never get a code 4 or code 5. It will return with the error
      "exit called" or something. I'll get around to this some day. If you
      see this, it 99% means your votes data is corrupted - so you'll have
      bigger things to worry about than whether or not VoteModule works...

      VoteModule will still get upset if no votes file exists, it will moan
      about CastAVote not 'seen' by the filer. However it will now load votes
      data with 0 votes without crashing.




VoteModule_SaveVotes                                      (SWI &16F01)

   Save the votes data to disc.

   On entry
      R1 = Pointer to filename.
           No filename will default to "<CastAVoteA$Dir>.Data.Data".

   On exit
      R0 = Operation status:
            1 - File saved okay.
            2 - Save failed. You have to find out why.

   Use:
      VoteModule_SaveVotes will save the votes data to the specified file, or
      default if no name is specified.

      In the future, this command may return with '3' in R0 if no filename is
      given. To 'ensure' the file, do not use this command, use
      VoteModule_Refresh in preference.




VoteModule_Refresh                                        (SWI &16F02)

   Save the votes data to disc to the default file ('ensure').

   On entry

   On exit
      R0 = Operation status:
            1 - File saved okay.
            2 - Save failed. You have to find out why.

   Use:
      VoteModule_Refresh will save the votes data to the default file. This
      call should be used if you want to 'ensure' the file.
      Currently, some 'database altering' commands automatically refresh the
      database.
      However this should not be relied upon, and you should refresh yourself
      at selected times in your program.
      Refresh will abort with a failure if there is no data loaded.




VoteModule_GetVote                                        (SWI &16F03)

   Return a pointer to the requested vote.

   On entry
      R0 = Required vote.

   On exit
      R0 = Operation status:
            1 - If okay.
            2 - If vote is out of range.
      R1 = Data:
            If R0 = 1 then R1 is a pointer to the data block.
            If R0 = 2 then R1 is the number of votes in the database.

   Use:
      VoteModule_GetVote will return a pointer to a data block (if okay)
      which contains the vote you requested.

      The data block should be 1024 bytes in length, in the form:

         struct votebuffer                /* BASIC equivalents:
         {  char creator[61];             /* block%+0
            char system[61];              /* block%+61 
            int  voted_for;               /* block%!124
            char question[61];            /* block%+128
            char software_opts[61];       /* block%+189
            char addinfo_one[61];         /* block%+250
            char addinfo_two[61];         /* block%+311
            char addinfo_three[61];       /* block%+372
            char option_one_text[61];     /* block%+433
            int  option_one_rslt;         /* block%!496
            char option_two_text[61];     /* block%+500
            int  option_two_rslt;         /* block%!564
            char option_three_text[61];   /* block%+568
            int  option_three_rslt;       /* block%!632
            char option_four_text[61];    /* block%+636
            int  option_four_rslt;        /* block%!700
            char option_five_text[61];    /* block%+704
            int  option_five_rslt;        /* block%!768
            char option_six_text[61];     /* block%+772
            int  option_six_rslt;         /* block%!836
            char option_seven_text[61];   /* block%+840
            int  option_seven_rslt;       /* block%!904
            char option_eight_text[61];   /* block%+908
            int  option_eight_rslt;       /* block%!972
            char reserved_data_block[24]; /* block%+976  *UNUSED*
            char vote_is_local[1];        /* block%?1000
            char vote_is_hidden[1];       /* block%?1001 *UNUSED*
            char vote_is_inaccessible[1]; /* block%?1002 *UNUSED*
            char vote_is_locked[1];       /* block%?1003 *UNUSED*
            char vote_flags_other[12];    /* block%?1004 *UNUSED*
            int  vote_data_crc;           /* block%!1016
            char vote_data_key[1];        /* block%?1020 *UNUSED*
            char reserved_developer[1];   /* block%?1021 *UNUSED*
            char reserved_user[1];        /* block%?1022 *UNUSED*
            char vote_datafile_type[1];   /* block%?1023 *UNUSED*
         };

      The data block is designed to fit C, not BASIC, thus the odd offsets.
      If you use the data block offsets defined, all should work.

      The '*UNUSED*' strings are not defined at this time, so you shouldn't
      assume anything about their contents.

      You must remember that the strings are zero-terminated, and the entire
      block could well not contain any newlines.

      You should use a function such as the one below to extract the text,
      which will extract text terminated with any control character.

         DEFFNextracttext(block%)
           LOCAL s$
           s$=""
           WHILE (?block%>31)
             s$+=CHR$(?block%) : block%+=1
           ENDWHILE
         =s$

      reserved_data_block
         Currently this can contain anything. The contents of which should
         not be relied upon.

      vote_is_local / vote_is_hidden / vote_is_inaccessible
      and vote_is_locked
         These flags determine the 'status' of the vote, in the following
         form:
            Local        - Will NOT be exported via NVP.
                           This is being implemented.
            Hidden       - Will NOT be shown to voters, creator can see.
            Inaccessible - Creator cannot access it either, higher than
                           hidden.
            Locked       - User cannot delete their own vote.
         The value of these elements should be read with the byte indirection
         operator '?'. 
         However, steps will be taken to ensure the last byte of 
         vote_flags_other is zero, so you should be able to read the
         whole lot as a string, if that would be easier.

         The flags are given values corresponding to the Latin1 (ISO 8859/1)
         character set, as follows:
            ''  (  0) - Old VoteModule in use, doesn't support flags yet.
            'X' ( 88) - Flags not yet supported.
            'Y' ( 89) - Flag is set.
            'N' ( 78) - Flag is unset.
            '?' ( 63) - Flag had peculiar/unexpected value.
            '¤' (164) - Old style vote, no flags found.
         Other values may be introduced in the future. If you receive an
         unknown value, you should provide a sensible fallback (usually
         assuming the flag IS set if not 'X' or 'Y' or '¤').

      vote_flags_other
         The contents of these bytes should not be relied upon. However the
         protocol defines that the very last byte will always be zero, so you
         can, if you wish, treat the flags as a zero-terminated string
         instead of reading them individually.

         If you do read the flags as a string, you should not assume any
         particular string length. The maximum will be sixteen characters,
         the minimum could be a zero-length string.

      vote_data_crc
         In the future, the vote data may be passed through OS_CRC to ensure
         that what went in is the same as what is coming out. The CRC will
         count from 0 to 1015 (missing the CRC and key words). Upon a failed
         CRC, you should retry reading the data and if it fails, reload the
         database and retry. If it still fails, print up whatever dire
         warning you think appropriate. :-)

         Please have the sense to trap '0' CRCs in your code...
         Some code will try to apply CRCs, but these values do not
         necessarily mean anything and should therefore be ignored.

      vote_data_key
         In the future, there will be a security option to keep the database
         encrypted whilst on disc, though valid information will be passed
         via the SWI. This is the 'key' used. The algorithm will remain
         undocumented.

      reserved_developer / reserved_user
         These are reserved for developer/user use, and will be allocated on
         a first-come-first-served. You may be allocated a bit inside the
         byte, with 15 bits up for grabs. You should not rely on the contents
         of these bytes unless you are aware of the significance. When
         setting your bits, don't alter any other bits...

         bit 0, reserved_developer, means "vote is tagged with VoteEdit".

      vote_datafile_type
         Informs the user of what file type is in use...
            0 - File type unknown or typing not supported.
            1 - Pre-2.05 file type (Ancient).
            2 - Pre-3 file type (Old).
            3 - Version 3 file type (Current).
            4 - PC variant of the Version 3 file type.
            5 - NatVoter file type (definition deprecated).
            6 - Enhanced version 3 file type (spec unfinished).
         Note, just because a file type is listed, it does NOT mean that
         VoteModule can load it. You should be expecting '0' or '3' or '6'...

         This version of VoteModule returns '3', as it can only read type 3
         files.




VoteModule_PutVote                                        (SWI &16F04)

   Add a vote to the database.

   On entry
      R0 = Pointer to vote block.

   On exit
      R0 = Operation status:
            1 if added okay.
            2 if overflow (more than 220 votes).
            10 if creator < 1 character
            11 if question < 1 character
            12 if option_one_text < 1 character
            13 if option_two_text < 1 character
            14 if software_opts has no egg ('¤' - denotes flags)


   Use:
      VoteModule_PutVote will take your pointer and try to update the votes
      database by adding the new vote to the END of the current database.

      It is assumed by VoteModule that:
         * Your data block is in EXACTLY the same format as for
           VoteModule_GetVote (above).
         * You have checked to ensure the vote is valid (two options etc).
         * That the strings are zero-terminated.

      If successful, the votes data file will be refreshed - whether you like
      it or not. :-)
      That is a 'peace of mind' feature.




VoteModule_AddVoteOpt                                     (SWI &16F05)

   Update a specified option.

   On entry
      R0 = Question.
      R1 = Option.
      R2 = Pointer to user's heap string [not coded yet].

   On exit
      R0 = Operation status:
            1 - If updated okay.
            2 - If invalid vote number.
            3 - If invalid option number.
      R1 = Set to zero [not coded yet].
      R2 = Pointer to updated user's heap string [not coded yet].

   Use:
      VoteModule_AddVoteOpt will update the specified option and totals for
      the specified question.

   An option is considered invalid if the option text is zero length. A
   question is considered invalid if it does not exist. :-)
   The username pointer / heap pointer are not currently used.
   If successful, the option count and totals will be updated. The database
   is not refreshed to disc.




VoteModule_GetVotesCount                                  (SWI &16F06)

   Return the number of votes in the database.

   On entry

   On exit
      R0 = Number of questions.
      R1 = Total number allowed, will reply '220'.

   Use:
      This is so you can quickly and easily see how many votes there are in
      the database.




VoteModule_Statistics                                     (SWI &16F07)

   Return some useful (?) information.

   On entry

   On exit
      R0 = Number of questions.
      R1 = Memory reserved for database.
      R2 = Pointer to default filename string.
      R3 = Unsaved_data.
      R4 = Module version, #.xx
      R5 = Module version, x.##
      R6 = Pointer to start of database memory.

   Use:
      This replies some useful and not-so-useful information...

      Number of questions
         The number of active questions.

      Memory reserved for database
         This is worked out 'by hand' and does not include space reserved for
         the unused components. It is a 'rough estimate' value.

      Pointer to default filename string.
         This is so you can read off the default filename. If you are clever,
         you may be able to patch in a different filename for the module to
         use. This is NOT recommended. The size allocated is 32 bytes,
         which must include a zero-terminator. Repeat, NOT recommended.
         :-)

      Unsaved_data
         '1' if there is unsaved data, or '0' if not. If there is unsaved
         data, you should 'refresh' (VoteModule_Refresh).

      Module version, #.xx
         Replies with the major version number.

      Module version, x.##
         Replies with the minor version number. Assume the minor number is
         two digits long with possible leading zero.
         Therefore '2' and '7' would be '2.07' or '1' and '23' would be
         '1.23'.

      Pointer to start of database memory.
         This points to where the database memory starts. If you know how,
         you could look directly in the memory for some information. This
         VoteModule does not apply any checking on the data, but future
         versions may maintain a CRC on the database - so don't even attempt
         to 'patch in' your own votes or change anything to do with the votes.
         Votes are 1024 bytes long, so the offsets are simple to calculate.




VoteModule_VoteOp                                         (SWI &16F08)

   Miscellaneous additional functions that VoteModule provides.

   On entry
      R0 = Operation:
            2 - Delete vote.
            3 - Compare vote.
            5 - Return heap entry.
            6 - Update heap entry.
            7 - Delete vote in all heap entries.
            8 - Decrement in all heap entries.

            9 - Increment in all heap entries.
            10 - Tidy all heap entries.
      R1++ = Depends on R1.

   On exit
      Depends on R0.



   2 - Delete vote:
      Entry R1 = Vote to delete.
      Exit  R0 = -1 if specified vote more than questions in database.
               1 if successful.
               2 if cannot open temporary file.
               3 if cannot open votes data file.

      This will delete the specified vote. It works by dumping all votes,
      except the specified, to a file and then reloading that file.



   3 - Compare vote:
      Entry R1 = String.
      Exit  R0 = Vote number or '-1'.

      Compare first 30 characters of string passed in R1 and return a vote
      number, or '-1' if no match. This is intended for NVP to facilitate
      duplicate checking. Search is case insensitive.



   5 - Return heap entry:
      Entry R1 = User name.
      Exit  R0 = 1 if successful.
                 2 if "HasVoted" file cannot be opened.
                 3 if username too long (maximum 35 characters).
                 4 if username not found. Returns blank heap entry.
      Exit  R1 = Heap entry (may be "").

      This fetches and returns the heap entry for the specified user. This
      saves you needing to perform file operations on the "HasVoted" file.



   6 - Update heap entry:
      Entry R1 = Username.
      Entry R2 = Heap entry string.
      Exit  R0 = 1 if successful.
                 2 if "XHasVoted" cannot be opened.
                 3 if (new) "HasVoted" cannot be opened.
                 5 if username too long (maximum 35 characters).
                 6 if heap entry too long (maximum 220 characters).

      This saves the heap entry for the specified user, again preventing you
      from having to access the HasVoted file.

      If the operation fails, you must check if a file called
      "XHasVoted" exists. If it does, you should rename this as "HasVoted".

      What happens: "HasVoted" renamed "XHasVoted", then data copied from
      "XHasVoted" to (new) "HasVoted". Updated if necessary or just directly
      copied. If no data to be updated, the specified data is appended to the
      end of the (new) "HasVoted" file. Then "XHasVoted" is deleted.



   7 - Delete vote in all heap entries.
      Entry R1 = Vote specifier.
      Exit R0  = 1 if successful.
                 2 if "XHasVoted" cannot be opened.
                 3 if (new) "HasVoted" cannot be opened.
                 4 if out of range.
                 If this replies with 2 or 3, read the note for action 6
                 (above).

      This command deletes the specified vote entry through the entire
      voted-on heap.
      It does not, however, shift the vote heap subsequent entries down.

      The formal protocol is to:
         1. Delete the vote.
         2. Delete the voted-on entry.
         3. Decrement following voted-on entries.
      Due to the way operation 3 works, a clever person could miss out step 2
      thus saving a little bit of time.



   8 - Decrement in all heap entries.
      Entry/exit parameters as for action 7 (on previous page).

      This does exactly the reverse of action 8 (above).
      You should call this after deleting a vote in order to realign the heap.
      Failure to do so will mean that voted-on entries for vote #17 (for
      example) no longer point to vote#17. This action has not been fully
      tested, but has been rewritten for v1.18.



   9 - Increment in all heap entries.
      Entry/exit parameters as for action 7 (above).

      This takes the vote number pointed so in R1 and increments all heap
      entries after that vote.

      For example:
         Vote data = 12345678
         You insert a blank vote in position 5 and call this program with
         input '5'.
         Vote data = 12346789
      The use of this command is not recommended as whilst blank votes
      can be created, they are technically invalid. This action has not been
      fully tested, but has been rewritten for v1.18.



   10 - Tidy all heap entries.
      Entry/exit parameters as for action 7 (above). R1 ignored.

      Tidying consists of:
         a.  Deleting all blank lines in the file.
         b.  Rearranging the voted-on entries into numerical order.

      Future versions of this may 'loose' any entries referring to higher
      numbered votes than currently exist in the votes database.

      This function is automatically performed by actions 7, 8 and 9 so you
      don't need to explicitly call this after, say, deleting a vote.




VoteModule_AmendVote                                      (SWI &16F09)

   Amend an existing vote in the database.

   On entry
      R0 = Pointer to data block.
      R1 = Question.

   On exit
      R0 = Operation status:
             1 if added okay.
             2 if overflow (more than 220 votes).
            10 if creator < 1 character
            11 if question < 1 character
            12 if option_one_text < 1 character
            13 if option_two_text < 1 character
            14 if software_opts has no egg ('¤' - denotes flags)

   Use:
      VoteModule_AmendVote will take your pointer and try to update the
      specified question.

      This is very much like VoteModule_PutVote, except you can
      specify any question number.

      There is a possible problem with the vote being changed whilst it is
      loaded in CastAvote, therefore CastAVote will still refuse to operate
      when VoteEdit is loaded.

      It is assumed by VoteModule that:
         * Your data block is in EXACTLY the same format as for
           VoteModule_GetVote (above).
         * You have checked to ensure the vote is valid (two options etc).
         * That the strings are zero-terminated.

      The easiest way is to VoteModule_GetVote the data, amend it,
      then throw back the same data block.

      If successful, the data will be saved to the default file - but you
      should not rely upon this, use VoteModule_Refresh...




VoteModule_GetVoteHeader                                         (SWI &16F0A)

   Return a pointer to ONLY the vote header.

   On entry
      R1 = Question.

   On exit
      R0 = Operation status:
            1 - If okay.
            2 - If question number is invalid.
      R1 = Pointer to data block.

   Use:
      VoteModule_GetHeader will fill a simple 128 byte data block with only
      the header information, in the form:

         struct headerblock       /* BASIC equivalents:
         {  char creator[61];     /* block%+0
            char question[61];    /* block%+61
            int  question_number; /* block%!124
         };

      This is to allow for 'quick' searching. It is also used by CastAVote in
      the "List Votes" option.

*Commands

*VoteModule_Statistics

   Prints some statistics on VoteModule.

   Syntax
      *VoteModule_Statistics

   Parameters
      None.

   Use
      This prints some rudimentary statistics, of which this is an example:

         BudgieSoft VoteModule version 1.18 ; to support CastAVote.

         VOTES DATABASE LOADED       : Yes.
         NUMBER OF VOTES IN DATABASE : 140.
         FILE FORMAT                 : Normal.
         UNSAVED DATA?               : No.
         MODULE FULLY OPERATIONAL    : Yes.

         Coded by                    : Richard Murray
         With the assistance of      : Richard Sargeant, Keith Hall,
                                       Rick Decker and Robin Abecasis.
         Notable testers and thanks  : John Stonier, Steve Pursey, Robin
                                       Abecasis, Keith Hall, Dane Koekoek,
                                       DaviD Dade/Dave Coleman and Chris
                                       Jackson.
   Example
      *VoteModule_Statistics

   Related commands
      None

   Related SWIs
      VoteModule_Statistics

   Related vectors
      None




*VoteModule_Refresh

   Refresh the database.

   Syntax
      *VoteModule_Refresh

   Parameters
      None.

   Use
      This calls VoteModule_Refresh, and prints a simple message to
      indicate okay or fail. If you don't want textual replies, use the SWI
      "VoteModule_Refresh".

   Example
      *VoteModule_Refresh

   Related commands
      None

   Related SWIs
      VoteModule_Refresh

   Related vectors
      None




*VoteModule_Extent

   Display memory usage information.

   Syntax
      *VoteModule_Extent

   Parameters
      None.

   Use
      This displays some information on the memory use and comparative memory use.

         This is the amount of memory required to hold the votes data...
         Size of 'votedata'   : 225280 bytes

         This is the amount of memory required, for the same, in BASIC...
            Strings  : Take 256 bytes per string, 15 per question.
            Integers : Take 4 bytes per integer, 9 per question.
            In all   : Multiply the above by 220 votes.
         Size of 'votedata' in BASIC : 852720 bytes (!)

      You should not assume the presence of this command, or anything about
      its output. Future versions of VoteModule may not have this command.

   Example
      *VoteModule_Extent

   Related commands
      None

   Related SWIs
      None

   Related vectors
      None

Writing a simple voting door

It is assumed that you are familiar with BASIC and your chosen door protocol. It is also assumed that the correct paths are set up and modules loaded (in !Run).

Firstly, you should define 1024 bytes of memory for the vote to be placed.

Next, SYS "VoteModule_LoadVotes". This call may fail if another CastAVote has been updating the votes, or the last program to use VoteModule failed to refresh.

SYS "VoteModule_GetVotesCount" TO ,questions% should be performed regularly.

To get a vote, simply call SYS "VoteModule_GetVote",vote% TO reply%,buffer% but be sure you can handle this call failing. If the call succeeds, then buffer% is filled with 1K of vote data. If the call fails (vote out of range etc) then buffer% will be returned an integer representing the current number of available votes.
Attempting to extract questions and options from a failed return will cause mondo crasho. Totally so gross. Grody to the max. Etc. Etc.

Adding a vote is the reverse of GetVote. You fill in the buffer and send it off to SYS "VoteModule_PutVote", buffer% TO reply% but be ready to catch errors and failed updates. You can work out how to increment an option. It really is dead-easy.

 

 

  File Formats

DATA

   The CastAVote file format is written with PRINT# and read with INPUT#.

   The basic file format is:
      STRING :  {header}
      INTEGER:  {number of questions}
      IF questions>0 REPEAT
         {question data (see below)}
      UNTIL all_questions_read
      STRING:   {footer}
      BYTES:    {extensions}
      STRING:   {2nd footer}

      The header string can be ignored. It is some backwards text to
      identify the file (backwards is readable in the file because PRINT#
      saves strings backwards).

      The questions number is safely anything from 0 to 220. If 0, the
      footer will immediately follow, otherwise question data will follow.

      question data is detailed below.

      The footer will be either "noitaunitnoC" for an extended format
      file or "elif fo dnE" for a basic version 3 file. These read as
      "Continuation" and "End of file" in Edit...

      The extensions are currently undefined. It is likely to be a
      block of, say, 128bytes per question. If you cannot handle extensions,
      but discover them, you should print up a warning saying "extension data
      will be lost if you continue" (unless you have a nifty way to preserve
      it).

      The 2nd footer will say "elif fo dnE".

      As the specification for the extended file format has not been drawn up,
      it is unlikely you will find any of the extended data. Of course, you
      don't really care as VoteModule will be loading the data for you...
      Won't it? :-)




      The question data is as follows:

      <creator>$      - Who created this question.
         1 to 60 characters.
         Example: "Hershey's Mr. Goodbar".

      <system>$       - system created the vote, BBS name and fidonet address.
         1 to 60 characters, fixed format.
         Example: "ArcTic BBS (at 2:254/86.0)"
         The format is pretty obvious from the above...
         Just to clarify for people that don't own NVP - the fidonet address
         is the address of the BBS server, not the address of the NVP
         tosser.

      <voted for>%    - Number of people to vote for this.
         An integer...

      <question>$     - The question.
         1 to 60 characters.
         Example: "What is the nicest chocolate?"

      <software/opt>$ - The software that created this vote and options.
         1 to 60 characters, fixed format.
         Example: "PizzaFileCreator v1.23 ; ¤0000000000000000"
         The "¤" and 16 digits after is necessary. See the flags
         detail (below) for more information.

      <addinfo line 1>$  - Additional information line 1.
      <addinfo line 2>$  - Additional information line 2.
      <addinfo line 3>$  - Additional information line 3.
         1 to 60 characters. Can be blank.
         Example: "Some additional info".

      <opt1text>$     - Option 1 text.
      <opt1res>%      - Option 1 results.
      <opt2text>$     - Option 2 text.
      <opt2res>%      - Option 2 results.
         Text: 1 to 60 characters. The first two options must be present.
         Text: Example: "Hersheys"
         Result: An integer. The collective addition of the results integers
                 should ideally equal the total. :-)


      <opt3text>$     - Option 3 text.
      <opt3res>%      - Option 3 results.
      <opt4text>$     - Option 4 text.
      <opt4res>%      - Option 4 results.
      <opt5text>$     - Option 5 text.
      <opt5res>%      - Option 5 results.
      <opt6text>$     - Option 6 text.
      <opt6res>%      - Option 6 results.
      <opt7text>$     - Option 7 text.
      <opt7res>%      - Option 7 results.
      <opt8text>$     - Option 8 text.
      <opt8res>%      - Option 8 results.
         These options can contain data (text: 1 to 60 characters) or be
         blank. If one option is blank, all further options should be blank.
         If an option is blank, it must have a "0" result.

      Options are 16 bytes (not yet coded):
         Bytes 1 and 2:
            Bit  0 - Vote is local (exclude from NVP activities).
                 1 - Vote is hidden from all except SysOp/VoteMaster & creator
                 2 - Vote is inaccessible to people unless "Registered" flag
                     set.
                 4 - Vote is locked (undeletable within CastAVote; use
                     VoteEdit).
                12/13/14/15 - CRC value in hexadecimal.
            All other bits/bytes unallocated.

         There is a rather gross 'fudge' planned. How can you CRC data that
         will be changed upon the value of the CRC? Simple. When the vote is
         loaded, the CRC data is extracted and stored in a variable. The four
         bytes reserved for CRC are filled with zeros and then the CRC
         is checked. The reverse is true for adding the CRC. If you need to
         check this yourself (you mean you don't use VoteModule!?) please be
         aware of this. Also, please have the sense to skip null CRCs (NVP
         will remove any CRC data in the file).

         If you use VoteModule, forget these quirks - it'll all be done for
         you.


Back to the CastAVote index
Copyright © 1999 Richard Murray