File instructions

Top  Previous  Next

This time we will learn all there is to know (well, almost) about Acknex's file instructions. 

Let's start by taking a look at the data types that are frequently used in games and can be manipulated by the engine.

 

aum66_workshop1

 

First of all, there's the mighty var, which can store numbers. These numbers can be read from a file using the instruction named file_var_read and can be written to a file using file_var_write. Sounds pretty easy, huh? Let's see an example which (how surprising!) is also the first code snippet from our workshop:

 

var var_handle;

var my_variable;

 

STRING variable_str = "      "; // the string can store up to 6 characters (6 digits for our number)

 

TEXT variable_txt =

{

       pos_x = 20;

       pos_y = 20;

       string (variable_str); // the string will store the "number" (a string which will be converted to a number afterwards)

       flags = VISIBLE;

}

 

function write_variable()

{

       variable_txt.visible = ON;

       inkey (variable_str); // store the input inside the string named variable_str

       my_variable = str_to_num(variable_str); // convert the string to a "real" variable and store it inside my_variable;

       var_handle = file_open_write ("variables.txt"); // open this file for writing (create it if it doesn't exist)

       file_var_write (var_handle, my_variable); // the variable was written inside the "variables.txt" file

       file_close (var_handle); // close the file, we won't be needing it from now on

}

 

on_f1 = write_variable;

 

As you can see, function write_variable( ) runs every time when the player presses the F1 key. The first line of code makes variable_txt visible. We want to do that because the following line waits for player's input and stores it inside variable_str, which happens to be the string associated to variable_txt.

 

Since inkey doesn't know anything about numbers, the string that holds our input (variable_str) is converted to a number using the str_to_num instruction, which was created especially for this purpose.

 

Let's rewind a bit: when we run the function, it shows the cursor and we can type in a number, but the input isn't numerical - it's a string that looks like "123" if we have typed in "123". The third line of code converts "123" to 123, a real number, which can be used for math operations, and so on.

 

Time to examine the last 3 lines inside the function; we are going to use similar code in our games many times:

 

       var_handle = file_open_write ("variables.txt"); // open this file for writing (create it if it doesn't exist)

 

This line opens a file named "variables.txt" for writing and returns the result of the operation as a number, aka a handle, which uniquely identifies the file. If the operation is successful (the file can be created or if it exists already), our var_handle will be set to the numerical value which is assigned to the file; otherwise, if the file can't be created (because there isn't enough free space on the disk, etc), var_handle will be set to zero.

 

       file_var_write (var_handle, my_variable); // the variable was written inside the "variables.txt" file

 

As you can see, file_var_write has a simple syntax: it needs the name of the handle associated to the file and the name of the variable (or the value) that needs to be written inside the file.

 

       file_close (var_handle); // close the file, we won't be needing it from now on

 

This simple instruction closes the file with the specified handle. We have written our variable to variables.txt, so now we can safely close the file. If you didn't test the function yet, run script41.wdl, and then press the F1 key. Input a numerical value (I chose to input 123) and then press the Enter key; you should see something like this:

 

aum66_workshop3

 

Exit the engine; you will see a file named "variables.txt" inside the project's folder. Double click the file and you will see that the value that was input is written inside the file. Let's run script41.wdl one more time; the screen should look like this:

 

aum66_workshop4

 

Now press the F2 key and the screen will look like this:

 

aum66_workshop5

 

As you might have guessed, F2 runs the function that reads the value of the variable from variables.txt and displays it on the screen. Let's examine its code:

 

var stored_variable = 0; // this variable will store the value which was stored inside variables.txt

 

function read_variable()

{

       var_handle = file_open_read("variables.txt");

       stored_variable = file_var_read (var_handle);

       file_close (var_handle);

}

 

PANEL variable_pan

{

       pos_x = 70;

       pos_y = 20;

       digits (0, 0, "The value of the variable is: %.0f", *, 1, stored_variable);

       flags = VISIBLE;

}

 

on_f2 = read_variable;

 

The first line of code inside function read_variable( ) opens the file named "variables.txt" for reading and assigns it a handle named var_handle. In this case, it is the same variable we've used for the function that was writing the variable inside the file, because we don't plan to write and read at the same time to and from variables.txt.

 

The second line of code reads the variable from variables.txt (our var_handle points it to that file) and stores the result of the reading inside "stored_variable". We have got our number back, so we can use it as we want to. The last line of code closes the variables.txt file - we don't need it anymore.

 

I have used a panel with a digit in order to display the value of stored_variable, but you can do anything you want with it; as an example, you can set the proper high score for your game by writing it to a file before shutting down the game and reading it from the same file at game start.

 

How can you read or write many variables to a file without using hundreds of lines of code? The answer is simple: use loops which include file_var_read or file_var_write instructions inside them. Let's see an example right away: run the script file, press the "1" key and then shut down the engine; you will see a new file named numbers.txt inside the workshop41 folder. Here's the content of the new file:

 

aum66_workshop6

 

As you can imagine, I didn't use 100 file_var_write instructions in order to write these numbers; in fact, the code is quite simple:

 

var numbers_handle;

 

function write_100_numbers()

{

       var i = 1;

       numbers_handle = file_open_write ("numbers.txt"); // open this file for writing (create it if it doesn't exist)

       while (i <= 100)

       {

               file_var_write (numbers_handle, i); // file_var_write adds a "space" after each number automatically

               i += 1;

       }

       file_close (numbers_handle); // close the file, we won't be needing it from now on

}

 

on_1 = write_100_numbers;

 

We open the file for writing (no need to do that more than once!) and then we run a loop which calls the file_var_write instruction 100 times. Finally, we close the file.

 

That's pretty much it when it comes to writing and reading variables; let's move on to something a bit more complex: writing and reading strings.

 

var string_handle;

 

STRING string_str = "                    "; // the string can store up to 20 characters

 

TEXT string_txt =

{

       pos_x = 20;

       pos_y = 200;

       string (string_str); // player's input will be stored inside this string

       flags = VISIBLE;

}

 

function write_strings() // writes and appends strings

{

       string_txt.visible = ON; // make the text visible (displays the cursor as well)

       string_handle = file_open_append("strings.txt"); // open this file for appending (create it if it doesn't exist)

       while (1)

       {

               inkey (string_str); // store the input inside the string named string_str

               if (!str_cmpi(string_str, "end")) // if the player didn't type "end" yet

               {

                       file_str_write (string_handle, string_str); // the string is written inside the "strings.txt" file

                       file_asc_write (string_handle, 13); // write the following string on a separate line

                       file_asc_write (string_handle, 10); // using asc(13) = carriage return + asc(10) = line feed

                       str_cpy (string_str, ""); // reset the input string - makes the input look better

               }

               else // the player has typed "end"

               {

                       break; // so we exit the "while" loop

               }

       }

       file_close (string_handle); // close the file, we won't be needing it from now on

       sys_exit(NULL); // shut down the engine

}

 

on_f3 = write_strings;

 

As you can see, the things are just a bit more complex; I have defined a string named string_str which can store up to 20 characters and a text that uses it. Function write_strings( ), which runs when the player presses the F3 key, makes the text visible, and then opens the strings.txt file for appending.

 

Our previous example was using file_open_write, which (as you know) creates a new file or opens an existing file, but (please pay attention) erases its content if it exist. The new instruction - file_open_append - allows us to append new data to a file.

 

Want an example? Ok, let's imagine the following situation: you are creating a program that keeps a list of the great books that you've read. Right now, your books.txt file looks like this:

 

John Doe - There's a tree in each one of us

Jane Doe - The mirror behind the mirror

Joan Doe - A bit of monkey

 

And now you encounter this great new book named "The criminal keyboard", you read it and you decide to add its name to the books.txt file. If you would use file_open_write the list would look like this:

 

Jenny Doe - The criminal keyboard

 

All your previous book records (strings) would be lost! Fortunately, you can use file_open_append, which would add the last book to the existing ones, creating a books.txt file that looks like this:

 

John Doe - There's a tree in each one of us

Jane Doe - The mirror behind the mirror

Joan Doe - A bit of monkey

Jenny Doe - The criminal keyboard

 

With all this fresh info in mind, let's examine the function that knows how to write strings to a file one more time.

 

function write_strings() // writes and appends strings

{

       string_txt.visible = ON; // make the text visible (displays the cursor as well)

       string_handle = file_open_append("strings.txt"); // open this file for appending (create it if it doesn't exist)

       while (1)

       {

               inkey (string_str); // store the input inside the string named string_str

               if (!str_cmpi(string_str, "end")) // if the player didn't type "end" yet

               {

                       file_str_write (string_handle, string_str); // the string is written inside the "strings.txt" file

                       file_asc_write (string_handle, 13);

                       file_asc_write (string_handle, 10);

                       str_cpy (string_str, ""); // reset the input string - makes the input look better

               }

               else // the player has typed "end"

               {

                       break; // so we exit the "while" loop

               }

       }

       file_close (string_handle); // close the file, we won't be needing it from now on

       sys_exit(NULL); // shut down the engine

}

 

The loop waits until the player inputs a string, and then compares it with "end". If the input string isn't "end", we use file_str_write to write the string that was input to the strings.txt file. The following 2 lines use the file_asc_write instruction and make sure that each new string is written on a separate line inside the strings.txt file; the first line writes a byte with a value of 13 (carriage return) to the file, while the second instruction write a byte with a value of 10 (line feed).

 

It might look a bit complicated, but that's the safest method that generates a new line inside a file, in a similar fashion with what you get when you press the Enter key in your favorite word processor. The good news is that these two lines do their job in any version of Windows (and not only in Windows ;). We'll discuss more about file_asc_write a bit later, but  for now just remember to use these two lines, in this order, whenever you want to jump to a new line inside your data files.

 

Back to our loop; we've got the input string written to the file, two file_asc_write instructions that make the jump to a new line, and then we reset string_str by copying "" (nothing) to it, preparing it for a new input. This means that the player can input as many strings as he want and all of them will be written to the strings.txt file. When you get bored, just type "end" (without the quotes) and then press Enter - the "if" branch will stop running and the "else" branch will do its job: "break" will get us out of the loop, closing the strings.txt file and shutting down the engine.

 

I have used *.txt files for all my examples, but you can give your files any other names and extensions, such as hotinfo.dbl - the code will work if the functions that write the data and read it are using the same file name.

 

Aren't you anxious to test the new function? Run the script, and then press the F3 key to start it. Type a short phrase (I have limited its length to 20 characters, but you can easily increase the length of string_str), and then press Enter. Repeat the process several times, or until you get bored. Then, type in "end" (without using the quotes) and the engine will close the file, shutting down.

 

The project folder should now contain a new file named strings.txt. Here's the content of my file:

 

aum66_workshop7

 

Run the script again, press F3 one more time, and then add one or more strings. Type "end" to close the file; strings.txt should look like this now:

 

aum66_workshop8

 

As you can see, file_open_append has done its job; it has allowed us to add new data and it didn't damage the old data.

 

Let's examine the function that reads the strings from the strings.txt file now:

 

var eof_reached = 0; // will be set to -1 when the end of the file (eof) is reached

 

TEXT string_array = // creates an array of strings using a text

{

       pos_x = 300;

       pos_y = 200;

       strings = 20;

       string = "                    "; // stores up to 20 characters for each string

       string = "                    ";

       ..........................................

       flags = VISIBLE;

}

 

function read_strings()

{

       var n = 0;

       string_handle = file_open_read("strings.txt"); // open the strings.txt file

       while (eof_reached != -1) // the end of the file wasn't reached yet?

       {

               eof_reached = file_str_read(string_handle, string_array.string[n]); // then read a string from the file

               n += 1; // move on to the following string

       }

       file_close (string_handle); // all the strings are read here, so close the file

}

 

on_f4 = read_strings;

 

I have defined a text with 20 strings (I didn't display them all in the text definition above). This text will have a double role:

1) It will serve as a string array, storing 20 strings with up 20 elements each

2) It will display the strings that are read from the strings.txt file.

 

Function read_strings( ) opens the file named string.txt for reading, and then reads all the data from it until eof_reached is set to -1. There are situations when we don't know how many records we've got in a file, so file_str_read helps us by returning -1 as soon as the end of file is reached. As you can see, our loop runs until that happens, reading the strings from strings.txt and storing them inside string_array.string[0]... string_array.string[19]. You can use a larger text array if you want to, but I thought that 20 strings are enough for this example.

 

If the end of file was reached, file_str_read returns -1 and the loop stops, closing the file. Let's see if this is true; run the script41 file, and then press the F4 key:

 

aum66_workshop9

 

It has worked! Let's turn our attention to the most powerful file instructions: file_asc_write and file_asc_read. Why are they the most powerful instructions? Mainly because they can replace (with little coding) the file_var_write, file_var_read, file_str_write and file_str_read instructions without problems, while the opposite isn't true.

 

Where does this power come from? File_asc_write and file_asc_read are able to write and read bytes, so they aren't limited to reading and writing only numerical characters, letters and the symbols that can be found on a regular PC keyboard; they have access to the full, extended ASCII set. I have created a table that shows all the ASCII codes - take a look at it.

 

aum66_workshop2

 

If you've always wanted to write all sorts of strange symbols to a file, your time has come! Using these instruction you can (as an example) open a bitmap, change its color, and then save it. Or maybe you want to create a program which is able to zip and unzip files using c-script and these 2 instructions - you can certainly do that.

 

Our example isn't complicated: we will wait for the player to input a phrase, we will encrypt it, and then we will save it inside a file. Then, we will read it, decrypt it and display it. You can use this snippet to save some encrypted data: passwords, high scores, etc. Let's bring on the code:

 

var encrypted_handle;

var number_of_characters; // will store the number of characters for encrypted_str

 

STRING encrypted_str = "                                                  "; // the string can store up to 50 characters

STRING temp_str = "                                                  "; // temporary string with up to 50 characters

 

TEXT encrypted_txt =

{

       pos_x = 20;

       pos_y = 500;

       string (encrypted_str); // player's input will be stored inside this string

       flags = VISIBLE;

}

 

function encrypt_strings() // allows the player to input a string, encrypts it and writes it inside the encrypted.txt file

{

       var string_index = 0;

       var temp_number;

       encrypted_txt.visible = ON;

       encrypted_handle = file_open_write("encrypted.txt"); // open this file for writing (create it if it doesn't exist)

       inkey (encrypted_str); // store player's input inside the string named encrypted_str

       number_of_characters = str_len(encrypted_str); // get the length of the strings

       while (string_index < number_of_characters) // go through all the characters of the string, one by one

       {

               str_cpy (temp_str, encrypted_str); // first of all, copy encrypted_str to temp_str (we don't want to destroy it)

               str_clip(temp_str, string_index); // cut the needed number of characters from the beginning of the string

               str_trunc(temp_str, number_of_characters - string_index - 1); // and cut from the end as well

               temp_number = str_to_asc(temp_str); // now we've only got a single char in temp_str, so let's convert it to ascii

               temp_number += 100; // this part encrypts the numbers by adding 100 to them; feel free to use any number here

               file_asc_write (encrypted_handle, temp_number); // now write the encrypted bytes to the disk

               string_index += 1; // move on to the following character in the string

       }

       file_close (encrypted_handle); // close the file, we won't be needing it from now on

}

 

on_f5 = encrypt_strings;

 

As you can see, function encrypt_strings( ) does all the magic; it displays the encrypted_txt text, which will store the string input by the user inside its associated encrypted_str string, and then opens the encrypted.txt file for writing. I have used str_len to get the number of characters for the string that is input; the result is stored inside the variable named number_of_characters.

 

The "while" loop will run until all the characters are read, one by one. If (let's say) the player has input this string: "HELLO WORLD", the loop will run 11 times, processing each character this way:

 

aum66_workshop10

Let's take a closer look at the "while" loop:

 

       while (string_index < number_of_characters) // go through all the characters of the string, one by one

       {

               str_cpy (temp_str, encrypted_str); // first of all, copy encrypted_str to temp_str (we don't want to destroy it)

               str_clip(temp_str, string_index); // cut the needed number of characters from the beginning of the string

               str_trunc(temp_str, number_of_characters - string_index - 1); // and cut from the end as well

               temp_number = str_to_asc(temp_str); // now we've only got a single char in temp_str, so let's convert it to ascii

               temp_number += 100; // this part encrypts the numbers by adding 100 to them; feel free to use any number here

               file_asc_write (encrypted_handle, temp_number); // now write the encrypted bytes to the disk

               string_index += 1; // move on to the following character in the string

       }

       file_close (encrypted_handle); // close the file, we won't be needing it from now on

 

The first line inside the loop copies the original, encrypted string to temp_str. We do this because we'll chop the string quite a bit and we don't want to lose the original. As you can see from the picture, str_clip cuts the specified number of characters from the beginning of the string, while str_trunc cuts the specified number of characters from the end of the string.

 

I have used a variable named string_index in conjunction with str_clip to cut more and more characters from the beginning of the string, as the loop runs more and more times, and "number_of_characters - string_index - 1" to cut the rest from the end of the string, leaving a single character from temp_str. This means that, for my example, as the loop runs, temp_str will be set to "H", then "E", then "L", "L", "O", " ", "W" and so on.

 

               temp_number = str_to_asc(temp_str); // now we've only got a single char in temp_str, so let's convert it to ascii

 

The line above converts our character to its ASCII (numeric) code; we can't write (ASCII) bytes if we don't operate with ASCII codes, get it?

 

               temp_number += 100; // this part encrypts the numbers by adding 100 to them; feel free to use any number here

 

Believe it or not, this line does all the encryption; it simply adds 100 to the ASCII code of our character, making it unrecognizable because the "normal" ASCII codes, the ones that can be found on a PC keyboard, range from 0 to 127, and now they'd be translated from 100 to 227. You can use any other number here, as long as you make sure that the resulting values don't go over 255 (the biggest ASCII code value).

 

               file_asc_write (encrypted_handle, temp_number); // now write the encrypted bytes to the disk

               string_index += 1; // move on to the following character in the string

 

The last two lines inside the loop write the byte to the file which is associated with encrypted_handle (encrypted.txt) and tell the loop to advance to the next character in the string. Let's see the function running; press F5, type in a phrase of up to 50 characters, and then press the Enter key. Don't forget to shut down the engine.

 

aum66_workshop11

 

This is my phrase; now let's examine the content of the newly created encrypted.txt file:

 

aum66_workshop12

 

Looks pretty good, considering the amount of work that went into creating this function... I know that you must be tired, but let's examine the last function, the one that decrypts the text and displays it:

 

var decrypted_handle;

 

STRING decrypted_str = "                                                  "; // the string can store up to 50 characters

STRING temporary_str;

 

TEXT decrypted_txt =

{

       pos_x = 320;

       pos_y = 500;

       string (decrypted_str); // displays the decrypted string

       flags = VISIBLE;

}

 

function decrypt_strings()

{

       var n = 0;

       var temp_number;

       str_cpy(decrypted_str, ""); // reset the string

       decrypted_handle = file_open_read("encrypted.txt"); // open the file

       if (decrypted_handle) // the file exists?

       {

               while (n < file_length(decrypted_handle)) // read every byte until we reach the end of the file

               {

                       temp_number = file_asc_read(decrypted_handle); // read a byte

                       temp_number -= 100; // subtract the same "100" value from it

                       str_for_asc(temporary_str, temp_number); // convert it to a character

                       str_cat(decrypted_str, temporary_str); // and add it to the decrypted_str

                       n += 1; // move on to the following byte

               }

               file_close (decrypted_handle); // the job is done here, so close the file

       }

       else // the file doesn't exist, decrypted_handle is set to 0

       {

               str_cpy (decrypted_str, "Encrypted.txt doesn't exist. Press F5 to create it"); // display this message

               while (key_any) {wait (1);} // wait until all the keys are released

               while (!key_any) {wait (1);} // wait until a key is pressed

       }

}

 

on_f6 = decrypt_strings();

 

As you see, the function opens encrypted.txt for reading, and then it does something interesting: it checks if decrypted_handle is valid or not. I have to admit that I have cheated a bit while I was coding the previous functions; I have assumed that all the needed text files exist in the workshop41 folder. If you are an evil person, you can go and delete the variables.txt file, run the script and then press F2 without pressing F1 first - you should see an engine error message that looks like this:

 

aum66_workshop13

 

Ok, enjoy your moment... my code didn't take into account the fact that the variables.txt file might not be present. However, even if you are an evil person, you will not have too many things to laugh about my code for function decrypt_strings( ) because this one takes into account the possibility that the encrypted.txt file might not be available, and if this is the case (if decrypted_handle is zero) it reacts this way:

 

       else // the file doesn't exist, decrypted_handle is set to 0

       {

               str_cpy (decrypted_str, "Encrypted.txt doesn't exist. Press F5 to create it"); // display this message

               while (key_any) {wait (1);} // wait until all the keys are released

               while (!key_any) {wait (1);} // wait until a key is pressed

       }

 

As you can see, the code displays a message, waits until the player releases the keys on the keyboard, and then waits until the player presses a key on the keyboard. I chose to do this in order to make sure that the player sees the error message. Try to delete the encrypted.txt file, you evil one, and you won't be able to crash my function.

 

And now let's see what happens when the rest of us, the good guys, open the existing encrypted.txt file:

 

       if (decrypted_handle) // the file exists?

       {

               while (n < file_length(decrypted_handle)) // read every byte until we reach the end of the file

               {

                       temp_number = file_asc_read(decrypted_handle); // read a byte

                       temp_number -= 100; // subtract the same "100" value from it

                       str_for_asc(temporary_str, temp_number); // convert it to a character

                       str_cat(decrypted_str, temporary_str); // and add it to the decrypted_str

                       n += 1; // move on to the following byte

               }

               file_close (decrypted_handle); // the job is done here, so close the file

       }

 

First of all, we read the content of the file, byte by byte, until we reach the number that's equal with the length of the file. Yes, file_length will do just that - it will compute the length (in bytes) of a certain file. The first line inside the loop uses file_asc_read to read a byte and stores it inside temp_number; then, we subtract 100 from temp_number, in order to remove the "encryption" we've added. Finally, str_for_asc converts the numerical ASCII value to the proper, original character.

 

We are working with bytes here, converting them to single characters, so we need to use str_cat to glue all the characters together. This means that our decrypted_str will grow (if we refer to my previous example) this way as the loop runs more and more:

 

decrypted_str = "H"

decrypted_str = "HE"

decrypted_str = "HEL"

decrypted_str = "HELL"

decrypted_str = "HELLO"

decrypted_str = "HELLO "

decrypted_str = "HELLO W"

..............................

decrypted_str = "HELLO WORLD"

 

Let's test this last function! Run the script and then press the F6 key; if you have saved some encrypted data, you should see it decrypted in all its splendor:

 

aum66_workshop14

 

And this, my friend, is the end of our workshop. It has taken a bit longer than what I have estimated, but I hope that you feel pretty comfortable when it comes to Acknex's file instructions. Don't forget that the best way to learn programming is to... write programs, what else? Feel free to experiment with all these functions, and then write your own functions using the marvelous file_var, file_str and file_asc instructions.