Embedding+resources+in+different+folders

//by Richard Russell, July 2007//

Background
BBC BASIC for Windows defines two system variables **@dir$** and **@lib$** which contain the paths to two important directories; **@dir$** is the directory containing the program itself (whether a BASIC program file, .BBC or .BAS, or a 'compiled' executable, .EXE) and **@lib$** is the directory containing the standard library modules.

The importance of these variables is that it allows your program to access **resource files** without needing to refer to absolute locations in the local file structure, thereby allowing the program to run correctly even when moved into a different directory or onto a different PC. Typically you would load libraries from the **@lib$** directory and other resources (e.g. images, sounds, fonts etc.) from the **@dir$** directory or a sub-directory:

code format="bb4w" INSTALL @lib$+"WINLIB2" INSTALL @lib$+"ARRAYLIB" OSCLI "PLAY """+@dir$+"SOUNDS\MIDIFILE.MID""" OSCLI "DISPLAY """+@dir$+"IMAGES\BACKGROUND.BMP""" SYS "AddFontResource", @dir$+"FONTS\BBCWIN.FON" code The use of these system variables is particularly important when you want to 'compile' your program to a standalone executable, since resource files required by your program can be //embedded// within the executable in such a way that they will be extracted to the correct location(s) on the target PC when it is run.

For resource files located in **@dir$** or a subdirectory this is straightforward: when the executable is run the files are extracted to equivalent locations, relative to the directory containing the EXE, on the target machine. However with files located in **@lib$** things are not so straightforward, since the target machine probably won't have the directory heirarchy in which the library files normally reside (typically ).

It would not be appropriate to create this directory on the target PC (and indeed to do so would require admin privileges) so instead the embedded library files are copied to a temporary directory, and the **@lib$** system variable is set to point to that directory; the temporary directory and files are automatically deleted when the program quits. Therefore the program runs correctly even though **@lib$** contains a very different path from what it would have been when run in the Interactive Development Environment.

Advanced use
The above usage is standard, and is as described in the main BB4W help documentation. However in practice things may not be quite as simple as this might imply. For example, suppose you want to run your program from a //read only// medium such as a CD-ROM, or you want it to reside in a location on the target PC where the final user doesn't have write access. In either case when the program is run it won't be able to extract any resource files to **@dir$**, or a subdirectory, and it will fail.

One practical solution to the program residing in a location to which the final user doesn't have write access is to run the program, just once, after it has been loaded onto the target machine. By definition, the person who was able to load the program must also be able to run it, because he or she must have the appropriate privileges. When the final user then runs the program it will find the resource files **already there**, and in that event it will not try to copy them again and it won't matter that it doesn't have write access.

However there isn't such a simple solution to the read-only medium problem, which can only really be resolved by not storing any resource files in **@dir$**. Another reason why one might prefer not to copy the resource files into **@dir$** is that they then become visible for anybody to study at their leisure, and any confidential information or programming secrets they may contain would be accessible (see Hiding confidential data).

So, what to do about this? One solution is to store all the program's resource files in **@lib$** rather than in **@dir$**. This will work fine, but it's a bit inelegant and it means cluttering up the library folder with files that are not libraries and are specific to a particular application. A better solution is therefore to create subdirectories of **@lib$** for each program wishing to make use of this feature, and store its resources there. So instead of the code listed above one might end up with the following:

code format="bb4w" INSTALL @lib$+"WINLIB2" INSTALL @lib$+"ARRAYLIB" OSCLI "PLAY """+@lib$+"MYAPP\SOUNDS\MIDIFILE.MID""" OSCLI "DISPLAY """+lib$+"MYAPP\IMAGES\BACKGROUND.BMP""" SYS "AddFontResource", @lib$+"MYAPP\FONTS\BBCWIN.FON" code Here, the program's resources are stored in the subdirectory of the library folder.

If you don't like the idea of using the library folder at all you can go one step further and store the resources not in a subdirectory of **@lib$** but in a subdirectory of its parent:

code format="bb4w" INSTALL @lib$+"WINLIB2" INSTALL @lib$+"ARRAYLIB" OSCLI "PLAY """+@lib$+"..\APPDATA\MYAPP\SOUNDS\MIDIFILE.MID""" OSCLI "DISPLAY """+lib$+"..\APPDATA\MYAPP\IMAGES\BACKGROUND.BMP""" SYS "AddFontResource", @lib$+"..\APPDATA\MYAPP\FONTS\BBCWIN.FON" code Note the additional **** in each path.

When the program is run, and compiled, from the IDE the resources need to be stored in the relevant subdirectory of the BBC BASIC for Windows installation folder, for example at (admin privileges will be required to copy the files there).

You might be surprised to find that this technique works even when the program is compiled, since the resource files will be extracted not to the temporary directory created when the executable is run, but to a subdirectory of its parent. Fortunately, because of where the temporary directory is created, this is guaranteed to work (it wouldn't if you attempted to go one level further up in the directory heirarchy). However one side effect is that resource files stored this way won't be automatically deleted when the program quits.

Note that the [|Compile] utility won't let you add, manually, an embedded file in the parent directory of **@dir$** or **@lib$** because it will report a **Directory changed** error, however you can still do so either by means of the automatic program scan or using the **REM!Embed** [|compiler directive].