#!/usr/bin/perl -w

# pilot-template 1.31 : Create the template files for a PalmOS application
# Copyright (C) 1998 Ian Goldberg <iang@cs.berkeley.edu>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

use strict 'vars';
use MIME::Base64 qw(decode_base64 encode_base64);

print <<'EOC';

pilot-template 1.31 by Ian Goldberg <iang@cs.berkeley.edu>
modified for booger plugins by Mitch Blevins <mblevin@debian.org>

EOC

my $Usage = <<EOU;
Usage: $0 prcname appname iconname CrID
  prcname : The desired name for the .prc file
  appname : The desired name for the application (up to 31 chars)
  iconname: The desired (short) name to appear in the application launcher
  CrID    : The desired four-character Creator ID
             (This _must_ be unique for every application in the world;
	      get a creator id at
	      http://palmpilot.3com.com/devzone/crid/crid.html)

Example: $0 template.prc 'Template application' 'Test app' Tmpl
EOU

my $iconext = 'pbm';
if ($#ARGV >= 0 && $ARGV[0] eq '-pbitm') {
    $iconext = 'pbitm';
    shift @ARGV;
}

die $Usage unless $#ARGV == 3;

my ($prcname, $appname, $iconname, $crid) = @ARGV;

die $Usage unless length($crid) == 4 && length($appname) < 32;

## Strip the .prc, if any, from the prcname
$prcname =~ s/\.prc$//;

## Make sure none of the files already exists
my $f;
foreach $f ("Makefile", $prcname.".c", "button.bmp", "lilbutton.bmp",
		$prcname.".rcp", $prcname."Rsc.h", "callback.h", "booger.h",
        "replace_prctype.c") {
    die "File $f already exists - aborting.\n" if -e $f;
}

## Create the files
open(F, ">Makefile") or die "Cannot write Makefile: $!\n";
print F <<EOFILE;
## Makefile for $appname

TARGET = $prcname
APPNAME = "$appname"
APPID = "$crid"
EOFILE
print F <<'EOFILE';

VERSION = "1.0"
SED = sed

OBJS = $(TARGET).o
LIBS =

CC = m68k-palmos-coff-gcc
REAL_CC = gcc

CFLAGS = -Wall -g -O2

PILRC = pilrc

# if native gcc is installed...
REPLACE_PRCTYPE = replace_prctype
# uncomment to use the perl version
#REPLACE_PRCTYPE = ../replace_prctype.pl

OBJRES = m68k-palmos-coff-obj-res
NM = m68k-palmos-coff-nm
BUILDPRC = build-prc
PILOTXFER = pilot-xfer

all: $(TARGET).prc

install: all
	cp $(TARGET).prc ../../plugins

install-$(LANG): $(TARGET)-$(LANG).prc
	cp $(TARGET)-$(LANG).prc ../../plugins-$(LANG)

.S.o:
	$(CC) $(TARGETFLAGS) -c $<

.c.s:
	$(CC) $(CSFLAGS) $<

$(TARGET).prc: $(REPLACE_PRCTYPE) code0000.$(TARGET).grc code0001.$(TARGET).grc data0000.$(TARGET).grc pref0000.$(TARGET).grc rloc0000.$(TARGET).grc bin.res
	$(BUILDPRC) $(TARGET).prc $(APPNAME) $(APPID) code0001.$(TARGET).grc code0000.$(TARGET).grc data0000.$(TARGET).grc *.bin pref0000.$(TARGET).grc rloc0000.$(TARGET).grc
	./$(REPLACE_PRCTYPE) $(TARGET).prc BooG

$(TARGET)-$(LANG).prc: $(REPLACE_PRCTYPE) code0000.$(TARGET).grc code0001.$(TARGET).grc data0000.$(TARGET).grc pref0000.$(TARGET).grc rloc0000.$(TARGET).grc bin-$(LANG).res
	$(BUILDPRC) $(TARGET)-$(LANG).prc $(APPNAME) $(APPID) code0001.$(TARGET).grc code0000.$(TARGET).grc data0000.$(TARGET).grc *.bin pref0000.$(TARGET).grc rloc0000.$(TARGET).grc
	./$(REPLACE_PRCTYPE) $(TARGET)-$(LANG).prc BooG

code0000.$(TARGET).grc: $(TARGET)
	$(OBJRES) $(TARGET)

code0001.$(TARGET).grc: code0000.$(TARGET).grc

data0000.$(TARGET).grc: code0000.$(TARGET).grc

pref0000.$(TARGET).grc: code0000.$(TARGET).grc

rloc0000.$(TARGET).grc: code0000.$(TARGET).grc

EOFILE
print F <<EOFILE;
bin.res: \$(TARGET).rcp
EOFILE
print F <<'EOFILE';
	$(SED) -e 's/^VERSION .*/VERSION 1 $(VERSION)/' $(TARGET).rcp > $(TARGET).rcp.bak && cp $(TARGET).rcp.bak $(TARGET).rcp
	$(PILRC) $(TARGET).rcp .
	mv Tbmp1965.bin BooG1965.bin
	mv Tbmp1966.bin BooG1966.bin
	mv tSTR1967.bin BooG1967.bin
	mv tSTR1968.bin BooG1968.bin
	mv tSTR1969.bin BooG1969.bin
	touch bin.res

bin-$(LANG).res: $(TARGET)-$(LANG).rcp
	-rm -f *.res
	$(SED) -e 's/^VERSION .*/VERSION 1 $(VERSION)/' $(TARGET)-$(LANG).rcp > $(TARGET)-$(LANG).rcp.bak && cp $(TARGET)-$(LANG).rcp.bak $(TARGET)-$(LANG).rcp
	$(PILRC) $(TARGET)-$(LANG).rcp .
	mv Tbmp1965.bin BooG1965.bin
	mv Tbmp1966.bin BooG1966.bin
	mv tSTR1967.bin BooG1967.bin
	mv tSTR1968.bin BooG1968.bin
	mv tSTR1969.bin BooG1969.bin
	touch bin-$(LANG).res

$(TARGET)-$(LANG).rcp:
	$(SED) -f ../plugin-$(LANG).sed $(TARGET).rcp > $(TARGET)-$(LANG).rcp

replace_prctype: replace_prctype.c
	$(REAL_CC) -o $@ replace_prctype.c

$(TARGET): $(OBJS)
	$(CC) $(OBJS) -o $(TARGET) $(LIBS)
	! $(NM) -u $(TARGET) | grep .

send: $(TARGET).prc
	$(PILOTXFER) -i $(TARGET).prc

depend:
	makedepend -Y -I. *.c


clean:
	-rm -f *.[oa] *~ $(TARGET) $(TARGET)-*.rcp *.bin *.res *.grc

veryclean: clean
	-rm -f *.prc *.bak replace_prctype

EOFILE
close(F);

open(F, ">${prcname}.h") or die "Cannot write ${prcname}.h: $!\n";
print F <<EOFILE;
/* Error routines */
#define abort() ErrDisplayFileLineMsg(__FILE__, __LINE__, "")

#ifdef BOOGERDEBUG
#define ASSERT(a) if(!a) ErrDisplayFileLineMsg(__FILE__, __LINE__, "Assert")
#define ASSERTNOT(a) if(a) ErrDisplayFileLineMsg(__FILE__, __LINE__, "Assert")
#else  /* BOOGERDEBUG */
#define ASSERT(a)
#define ASSERTNOT(a)
#endif /* BOOGERDEBUG */


EOFILE
close(F);

open(F, ">${prcname}.c") or die "Cannot write ${prcname}.c: $!\n";
print F <<EOFILE;
/* Main code for $appname */

#include <Pilot.h>
#include "booger.h"
/* #include "callback.h" */

#include "${prcname}Rsc.h"
#include "${prcname}.h"


/* Get preferences, open (or create) app database */
static Word StartApplication(void) {
    return 0;
}

/* Save preferences, close forms, close app database */
static void StopApplication(void) {
}

/* The main processing */
/* Fill the kleenexP->booger struct and return 0 if no error */
static Word BlowNose(KleenexPtr kleenexP) {
    ULong creatorID;
    DmOpenRef dbR;
    DmSearchStateType searchstate;
    LocalID dbID;
    UInt cardNo;
    UInt index;


    /* Check for the correct version */
    if (!((kleenexP->version)&IBVERSION_ORIG))
        return boogerErrorVersionMismatch;

    /* Load new record info here (plugin-dependant) */
    creatorID = sysFileCToDo;

    /* Open the database */
    if (DmGetNextDatabaseByTypeCreator(true, &searchstate, 'DATA',
                            creatorID, true, &cardNo, &dbID)) {
        return 1;
    }
    dbR = DmOpenDatabase(cardNo, dbID, dmModeReadWrite);
    if (!dbR) return 1;

    /* Write the new record (be sure to fill index) */
    /* FIXME */
    index = 0;

    /* Close the database */
    DmCloseDatabase(dbR);

    /* Load the goto params */
    kleenexP->booger.cmdPBP = MemPtrNew(sizeof(GoToParamsType));
    if (!kleenexP->booger.cmdPBP) {
        abort();
        return 1;
    }
    ((GoToParamsPtr)(kleenexP->booger.cmdPBP))->dbCardNo = cardNo;
    ((GoToParamsPtr)(kleenexP->booger.cmdPBP))->dbID = dbID;
    ((GoToParamsPtr)(kleenexP->booger.cmdPBP))->recordNum = index;
    MemPtrSetOwner(kleenexP->booger.cmdPBP, 0);
    /* Load the other stuff */
    kleenexP->booger.cmd = sysAppLaunchCmdGoTo;
    if (DmGetNextDatabaseByTypeCreator(true, &searchstate, 'appl',
                            creatorID, true, &cardNo, &dbID)) {
        MemPtrFree(kleenexP->booger.cmdPBP);
        return 1;
    }
    kleenexP->booger.cardNo = cardNo;
    kleenexP->booger.dbID = dbID;

    /* Alls well that ends well... */
    return 0;
}

/* Main entry point; it is unlikely you will need to change this except to
   handle other launch command codes */
DWord PilotMain(Word cmd, Ptr cmdPBP, Word launchFlags) {
    Word err;

    switch (cmd) {
        case boogerPlugLaunchCmdBlowNose:
            err = StartApplication();
            if (err) return err;

            err = BlowNose((KleenexPtr)cmdPBP);
            if (err) return err;

            StopApplication();
            break;
        default:
            return sysErrParamErr;
            break;
    }

    return 0;
}
EOFILE
close(F);

open(F, ">${prcname}Rsc.h") or die "Cannot write ${prcname}Rsc.h: $!\n";
print F <<EOFILE;

// Booger resources
#define boogerID_button           6501
#define boogerID_lilbutton        6502
#define boogerID_plugname         6503
#define boogerID_gotobehavior     6504
#define boogerID_completebehavior 6505

// Alert for errors
#define alertID_error           6600

// IB Versions
#define IBVERSION_ORIG		0x0001
#define IBVERSION_PICTURE       0x0002

EOFILE
close(F);

open(F, ">${prcname}.rcp") or die "Cannot write ${prcname}.rcp: $!\n";
print F <<EOFILE;
#include "${prcname}Rsc.h"

// 14x14 Bitmap for the button
BITMAP ID boogerID_button "button.bmp"

// The little bitmap for the drop-up list
BITMAP ID boogerID_lilbutton "lilbutton.bmp"

// The application name string
STRING ID boogerID_plugname "Plugin Name"

// The Goto behavior string
// "a" = always Goto
// "n" = never Goto
// "o" = optional
STRING ID boogerID_gotobehavior "o"
STRING ID boogerID_completebehavior "n"

// Alert for errors
ALERT ID alertID_error
BEGIN
    TITLE "Error"
    MESSAGE "^1"
    BUTTONS "OK"
END

VERSION 1 "1.0"
EOFILE
close(F);

open(F, ">callback.h") or
    die "Cannot write callback.h: $!\n";

print F <<EOFILE;
#ifndef __CALLBACK_H__
#define __CALLBACK_H__

/* This is a workaround for a bug in the current version of gcc:

   gcc assumes that no one will touch %a4 after it is set up in crt0.o.
   This isn't true if a function is called as a callback by something
   that wasn't compiled by gcc (like FrmCloseAllForms()).  It may also
   not be true if it is used as a callback by something in a different
   shared library.

   We really want a function attribute "callback" which will insert this
   progloue and epilogoue automatically.
   
      - Ian */

register void *reg_a4 asm("%a4");

#define CALLBACK_PROLOGUE \
    void *save_a4 = reg_a4; asm("move.l %%a5,%%a4; sub.l #edata,%%a4" : :);

#define CALLBACK_EPILOGUE reg_a4 = save_a4;

#endif
EOFILE
close(F);

open(F, ">booger.h") or
    die "Cannot write booger.h: $!\n";

print F <<'EOFILE';
/* booger.h  version 'a' */
#include <Pilot.h>

/* Launch command to activate a booger plugin */
#define boogerPlugLaunchCmdBlowNose 0x9101

/* Error codes */
#define boogerErrorVersionMismatch 7000

/* Structure for the BoogerType returned to DiddleBug */
typedef struct {
    UInt cardNo;            /* cardNo */
    LocalID dbID;           /* dbID */
    Word cmd;               /* Launch flags */
    Ptr cmdPBP;             /* usually a GoTo params */
} BoogerType;
typedef BoogerType *BoogerPtr;

/* Repeat types */
enum repeatTypes {
    repeatNone,
    repeatDaily,
    repeatWeekly,
    repeatMonthlyByDay,
    repeatMonthlyByDate,
    repeatYearly
};
typedef enum repeatTypes RepeatType;

/* Repeat info type (same as DateBook) */
typedef struct {
    RepeatType repeatType;            /* daily, weekly, monthlyByDay, etc. */
    DateType repeatEndDate;           /* minus one if forever */
    unsigned char repeatFrequency;    /* i.e. every 2 days */
    unsigned char repeatOn;           /* monthlyByDay and repeatWeekly only */
    unsigned char repeatStartOfWeek;  /* repeatWeekly only */
} RepeatInfoType;
typedef RepeatInfoType *RepeatInfoPtr;


/* Structure for cmdPBP of boogerPlugLaunchCmdBlowNose */
/* DiddleBug is responsible for freeing the pointers upon return */
typedef struct {
    DWord reserved1;
    DWord reserved2;
    UInt version;           /* version of this launch param */
    UInt data_size;         /* not implemented yet... */
    VoidPtr data;           /* Will eventually hold sketch data */
    CharPtr text;           /* Text as typed in the pick field */
    CharPtr title;          /* Title of the sketch (if any) */
    CharPtr category;       /* Category of the sketch (if any) */
    ULong alarm_secs;       /* Alarm secons (zero if no alarm) */
    RepeatInfoPtr repeat;   /* Repeat info (in DateBook format) */
    SWord priority;         /* Priority of the reminder (default = 1) */
    BoogerType booger;      /* Filled by the BPlug to be read by DiddleBug */
    SWord is_complete;      /* Complete flag */
} KleenexType;
typedef KleenexType *KleenexPtr;

EOFILE
close(F);

open(F, ">replace_prctype.c") or
    die "Cannot write replace_prctype.c: $!\n";

print F <<'EOFILE';
#include <stdio.h>

void usage(char *progname) {
    fprintf(stderr, "usage: %s [filename.prc] [prctype]\n", progname);
    exit(1);
}

int main(int argc, char** argv) {
    char old_name[256];
    char type[5];
    unsigned long i=0;
    int c;
    FILE *prcfile, *orig_prcfile;


    if (argc < 2) usage(argv[0]);
    if (strlen(argv[2]) != 4) {
        fprintf(stderr, "%s: Error: prctype must be 4 characters\n", argv[0]);
        exit(1);
    }

    printf("%s: preparing to replace prc type with %s... ", argv[0], argv[2]);
    strncpy(type, argv[2], 4);
    strncpy(old_name, argv[1], 255);
    strncat(old_name, ".bak", 255);

    if (rename(argv[1], old_name)) {
        fprintf(stderr, "\n%s: could not rename %s\n", argv[0], argv[1]);
    }
    if (!(orig_prcfile = fopen(old_name, "r"))) {
        fprintf(stderr, "\n%s: could not open %s\n", argv[0], old_name);
        exit(1);
    }
    if (!(prcfile = fopen(argv[1], "w+"))) {
        fprintf(stderr, "\n%s: could not create %s\n", argv[0], argv[1]);
        exit(1);
    }

    while ((c = fgetc(orig_prcfile)) != EOF) {
        if (i == 60) c = (unsigned int) argv[2][0];
        if (i == 61) c = (unsigned int) argv[2][1];
        if (i == 62) c = (unsigned int) argv[2][2];
        if (i == 63) c = (unsigned int) argv[2][3];
        i++;
        fputc(c, prcfile);
    }
    fclose(prcfile);
    fclose(orig_prcfile);
    printf ("Done\n", argv[0]);
    return(0);
}

EOFILE
close(F);

open(F, ">button.bmp") or
    die "Cannot write button.bmp: $!\n";

print F decode_base64 <<'EOFILE';
Qk12AAAAAAAAAD4AAAAoAAAADgAAAA4AAAABAAEAAAAAADgAAAABAAAAAQAAAAIAAAACAAAAAAAA
AP///wD//AAA//wAAPz8AAD4fAAA8DwAAOAcAADDDAAAwwwAAOAcAADwPAAA+HwAAPz8AAD//AAA
//wAAA==
EOFILE
close(F);

open(F, ">lilbutton.bmp") or
    die "Cannot write lilbutton.bmp: $!\n";

print F decode_base64 <<'EOFILE';
Qk1iAAAAAAAAAD4AAAAoAAAADwAAAAkAAAABAAEAAAAAACQAAAABAAAAAQAAAAIAAAACAAAAAAAA
AP///wD//gAA/v4AAPx+AAD4PgAA8R4AAPg+AAD8fgAA/v4AAP/+AAA=
EOFILE
close(F);

print "Done.\n";
