PND Cookbook

From Pandora Wiki
Revision as of 17:13, 23 April 2011 by T4b (talk | contribs) (Corrected <source> tag)
Jump to: navigation, search

Using a start-up script

In an ideal world, your application will be completely self-contained and can be run directly without any kind of set-up. It is however often necessary to tweak settings or request input from the user before starting your application. This is easily achieved by having the PND specify a shell script as its executable, which then in turn runs the application. The general structure of such a script is:

#!/bin/sh

# Do stuff here...

./my_app

# Clean-up

The first line identifies the file as a shell script, run using "/bin/sh" as the shell. The other lines beginning with a "#" are comments. Before the application is started, parameters can be gathered and/or set. The application itself then gets run by the "./my_app" line, which means "execute the file my_app in the current directory". When the application exits, it may be necessary to restore previous settings, delete temporary files, or perform some other kinds of cleaning up.

Avoiding NAND writes by setting $HOME

Many applications write configuration data to the user's home directory by default. This is not desirable on the Pandora as this directory resides on the NAND filesystem. It is however often possible to fool an application into writing to a different directory by setting the $HOME environment variable.

export HOME=`pwd`
export HOME=$(pwd)

Providing a default configuration

If your application uses a configuration file, you'll usually want to include this in the PND. However, if you simply leave this file in the root of the PND, it will effectively be read-only, preventing the application or user from writing to it. A simple solution is to provide a default configuration file with a different name, and copy this the first time the application is run. The file will then be created under appdata, and can therefore easily be edited.

# Method 1: check if file exists, otherwise copy default.
if [ ! -f app.conf ] ; then
    cp default.conf app.conf
fi

# Method 2: Same as above, less typing.
[ -f app.conf ] || cp default.conf app.conf

# Method 3: using a non-destructive copy.
cp --no-clobber default.conf app.conf

Nub Configuration

This is a little (but not much!) lengthier - see the page on PND nub modes.

Making writable directories

If your application requires that directories be writable for the application, don't put them in the PND, but create them at run-time:

# Bad! This will cause an error after the first run, as the directory will already exist.
mkdir saved_data

# Better to use "-p" which won't complain about existing directories.
mkdir -p saved_data

# Added bonus: this allows you to create whole trees in one go.
mkdir -p saved_data/foo saved_data/bar/with/deep/sub/dirs

Loading shared libraries

If your application requires shared libraries which aren't part of the standard firmware, they can be included in the PND along with the executable. In order for the system to be able to find the libraries, it must be told which directories to search. This can be done by setting the LD_LIBRARY_PATH environment variable. For example:

export LD_LIBRARY_PATH=`pwd`       # Load libraries in the root directory of the PND.
export LD_LIBRARY_PATH=`pwd`/libs  # Load libraries in the "libs" subdirectory of the PND.

Getting a file/directory from the user

Its often necessary to ask the user for the location of a file or directory. This may be because your application requires files which aren't included in the PND itself, or simply because you application doesn't have a built in file picker. The commands below will prompt the user for a file, then run "my_app" with that file as an argument. Note the use of different sorts of quotes which is important for proper operation and handling spaces in file names.

FILE="`zenity --file-selection --title='Select a File'`"
./my_app "$FILE"

Saving a file/directory name

If your application always asks for a file at start-up, it's handy to remember the location for next time. This can be done by writing the name of the directory to a file in appdata, and reading it back next time the application is run.

SAVEDIR=./dir.saved    # Name of the file we'll save the directory name to

# Did we save a directory last time? If so, read it into a variable.
DIR=`cat $SAVEDIR 2> /dev/null`

# Remember where we are right now, as we'll need to come back later.
PNDDIR=`pwd`

# Does the directory still exist?
if [ -d "$DIR" ] ; then
    # Yes, go to that directory before Zenity is started.
    cd "$DIR"
else
    # No, try to go to /media (where the SD cards are mounted)
    # or if that fails, go to "/" as a last resort.
    cd /media 2> /dev/null || cd /
fi

# Get a file name form the user. Our current directory is now that which
# was saved, so that's where Zenity will start.
FILE=`zenity --file-selection --title="Select a file"`

# If the file selector failed, or the user hit cancel, exit now.
[ $? -eq 0 ] || exit 1;

# Go back to the PND's mount point.
cd $PNDDIR

# Save the file's directory for next time.
dirname "$FILE" > $SAVEDIR

# Run our app with the file as an argument.
./my_app "$FILE"

Checking a file's contents

Maybe your application requires some file which cannot be distributed with the PND, either for legal reasons or due to size limitations. A prime example of this is BIOS files used for emulators. Simply copying the file to the current directory will be enough to save it to your program's appdata, but it can also be useful to check the contents of the file are indeed what your application expects.

One way of doing this is to check the file's contents using a hash. A hash is a mathematical formula which can be used to create a "fingerprint" of a file. Two identical files will always generate the same hash value, so knowing this one value is enough to check if two files match. The MD5 hash is a popular choice, and can be easily generated for a given file like so:

$ md5sum bios.img
cfcfd01f1b0dfa97f07cb286b2942dc2  bios.img

Save this output to a text file named bios.md5 and put it in your PND root directory. The following commands can then be used in your start-up script to copy a file specified by the user, and check it's hash.

BIOS=bios.img       # The name of our BIOS file

# Check if the BIOS file already exists.
if [ ! -f ./$BIOS ] ; then
    # Not found, so get a file name from the user.
    cp "`zenity --file-selection --title="Select your $BIOS file"`" $BIOS
    if [ $? != 0 ] ; then
        # User hit cancel, bail out.
        zenity --error --text="Sorry, emulator cannot run without a BIOS file."
        exit 1
    fi

    # Check the MD5 hash of the given file against our known-good value.
    # If the match fails, ask the user if they'd like to try it anyway.
    md5sum -c bios.md5 || zenity --question \
        --text="BIOS does not appear to be the correct version. Use it anyway?"

    if [ $? != 0 ] ; then
        # File did not match hash, and the user opted not to try it anyway,
        # so remove the copied file and bail out.
        rm -f $BIOS
        exit 1
    fi
fi

# Run our emulator with the BIOS file as an argument.
./my_emu "$BIOS"