]> git.sur5r.net Git - c128-kasse/blob - src/kasse.c
kasse: allow starting up without a printer
[c128-kasse] / src / kasse.c
1 /*
2  * RGB2R-C128-Kassenprogramm
3  * © 2007-2009 phil_fry, sECuRE, sur5r
4  * See LICENSE for license information
5  *
6  */
7 #include <stdio.h>
8 #include <conio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <cbm.h>
12 #include <c128.h>
13 #include <6502.h>
14
15 #include "general.h"
16 #include "config.h"
17 #include "kasse.h"
18 #include "credit_manager.h"
19 #include "c128time.h"
20 #include "print.h"
21 #include "version.h"
22 #include "vdc_patch_charset.h"
23 #include "globals.h"
24 // drucker 4 oder 5
25 // graphic 4,0,10
26
27 void print_item(BYTE i) {
28   char profit[10];
29   if (format_euro(profit, sizeof(profit), status.status[i].price) == NULL) {
30     cprintf("Preis %ld konnte nicht umgerechnet werden\r\n",
31             status.status[i].price);
32     exit(1);
33   }
34   textcolor(TC_YELLOW);
35   cprintf("%2d", i);
36   textcolor(TC_LIGHT_GRAY);
37   cprintf(": %-" xstr(MAX_ITEM_NAME_LENGTH) "s \xDD%s,   %3dx ",
38           status.status[i].item_name, profit, status.status[i].times_sold);
39 }
40
41 /* Hauptbildschirm ausgeben */
42 static void print_screen(void) {
43   BYTE i = 0;
44   char *time = get_time();
45   char profit[10];
46   clrscr();
47   if (format_euro(profit, sizeof(profit), money) == NULL) {
48     cprintf("Einnahme %ld konnte nicht umgerechnet werden\r\n", money);
49     exit(1);
50   }
51   textcolor(TC_CYAN);
52   cprintf("C128-Kassenprogramm (phil_fry, sECuRE, sur5r, mxf) " GV "\r\n");
53   textcolor(TC_LIGHT_GRAY);
54   cprintf("\r\nUhrzeit:     %s (wird nicht aktualisiert)\r\n"
55           "Eingenommen: %s, Verkauft: %ld Dinge, Drucken: %s\r\n",
56           time, profit, items_sold, (printing == 1 ? "ein" : "aus"));
57   textcolor(TC_LIGHT_GRAY);
58   cprintf("      \xB0"
59           "\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xB2"
60           "\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xB2"
61           "\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xB2"
62           "\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xAE"
63           "\r\n");
64   for (; i < min(status.num_items, 15); ++i) {
65
66     cprintf("      \xDD");
67     print_item(i);
68     cprintf("\xDD");
69
70     /* if we have more than 15 items, use the second column */
71     if ((i + 15) < status.num_items) {
72       print_item(i + 15);
73       cprintf("\xDD");
74     } else {
75       cprintf("              \xDD                \xDD");
76     }
77
78     cprintf("\r\n");
79   }
80   cprintf("      \xAD"
81           "\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xB1"
82           "\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xB1"
83           "\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xB1"
84           "\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xBD"
85           "\r\n");
86   textcolor(TC_YELLOW);
87   cprintf("   s");
88   textcolor(TC_LIGHT_GRAY);
89   cprintf(") Daten sichern                                  ");
90   textcolor(TC_YELLOW);
91   cprintf("g");
92   textcolor(TC_LIGHT_GRAY);
93   cprintf(") Guthabenverwaltung\r\n");
94   textcolor(TC_YELLOW);
95   cprintf("   z");
96   textcolor(TC_LIGHT_GRAY);
97   cprintf(") Zeit setzen         ");
98   textcolor(TC_YELLOW);
99   cprintf("f");
100   textcolor(TC_LIGHT_GRAY);
101   cprintf(") Freitext verkaufen      ");
102   textcolor(TC_YELLOW);
103   cprintf("q");
104   textcolor(TC_LIGHT_GRAY);
105   cprintf(") Beenden\r\n");
106 }
107
108 /*
109  * Prints a line and logs it to file. Every line can be at max 80 characters.
110  *
111  */
112 static void print_log(char *name, int item_price, int einheiten, char *nickname,
113                       char *rest) {
114   char *time = get_time();
115   char price[10];
116   /* Format:
117      Transaction-ID (Anzahl verkaufter Einträge, inklusive des zu druckenden!)
118      -- 6-stellig
119      Uhrzeit -- 8-stellig
120      Eintragname (= Getränk) -- 9-stellig
121      Preis (in Cents) -- 9-stellig
122      Anzahl -- 2-stellig
123      Nickname (falls es vom Guthaben abgezogen wird) -- 10-stellig
124      restguthaben (9-stellig)
125
126      + 7 leerzeichen
127      --> 48 zeichen
128      */
129   if (format_euro(price, sizeof(price), item_price) == NULL) {
130     cprintf("Preis %d konnte nicht umgerechnet werden\r\n", item_price);
131     exit(1);
132   }
133
134   sprintf(print_buffer, "%c[%3u] %s - %-" xstr(
135                             MAX_ITEM_NAME_LENGTH) "s - %s - %s - %d - an %s\r",
136           17, status.transaction_id, time, name, price, rest, einheiten,
137           (*nickname != '\0' ? nickname : "Unbekannt"));
138   status.transaction_id++;
139   print_the_buffer();
140 }
141
142 /* dialog which is called for each bought item */
143 static signed int buy(char *name, unsigned int price) {
144   BYTE matches = 0;
145   BYTE c, nickname_len;
146   int einheiten;
147   char nickname[NICKNAME_MAX_LEN + 1];
148   char rest[10];
149   struct credits_t *credit;
150
151   memset(nickname, '\0', sizeof(nickname));
152   memset(rest, '\0', sizeof(rest));
153
154   clrscr();
155   cprintf("Wieviel Einheiten \"%s\"? [1] \r\n", name);
156
157   einheiten = cget_number(1);
158
159   if (einheiten > 100 || einheiten < -100 || einheiten == 0) {
160     cprintf("\r\nEinheit nicht in [-100, 100] oder 0, Abbruch, dr" uUML "cke "
161             "RETURN...\r\n");
162     cgetc();
163     return 1;
164   }
165
166   cprintf("\r\nAuf ein Guthaben kaufen? Wenn ja, Nickname eingeben:\r\n");
167   {
168     BYTE i;
169     BYTE x;
170     BYTE y;
171     BYTE matches;
172     char *uniquematch;
173     input_terminator_t terminator;
174     while (1) {
175       terminator = get_input_terminated_by(INPUT_TERMINATOR_RETURN |
176                                                INPUT_TERMINATOR_SPACE,
177                                            nickname, sizeof(nickname));
178
179       /* Clear the screen from any previous completions */
180       x = wherex();
181       y = wherey();
182       for (i = 1; i < 7; i++) {
183         /* "Completion:" is longer than NICKNAME_MAX_LEN */
184         cclearxy(0, y + i, strlen("Completion:"));
185       }
186       gotoxy(x, y);
187
188       if (terminator != INPUT_TERMINATOR_SPACE) {
189         break;
190       }
191
192       matches = 0;
193       uniquematch = NULL;
194       for (i = 0; i < credits.num_items; i++) {
195         if (strncmp(nickname, credits.credits[i].nickname, strlen(nickname)) !=
196             0) {
197           continue;
198         }
199         matches++;
200         if (matches > 1) {
201           break;
202         }
203         uniquematch = credits.credits[i].nickname;
204       }
205       if (matches == 1) {
206         /* Display the rest of the nickname */
207         textcolor(TC_LIGHT_GREEN);
208         cprintf("%s", uniquematch + strlen(nickname));
209         textcolor(TC_LIGHT_GRAY);
210         strcat(nickname, uniquematch + strlen(nickname));
211       } else {
212         /* Multiple nicknames match what was entered so far. Abort and
213          * display all matches, then prompt the user again. */
214         char completion[NICKNAME_MAX_LEN + 1];
215         BYTE len = strlen(nickname);
216         x = wherex();
217         y = wherey();
218         cprintf("\r\nCompletion:\r\n");
219         matches = 0;
220         for (i = 0; i < credits.num_items; i++) {
221           if (strncmp(nickname, credits.credits[i].nickname, len) != 0) {
222             continue;
223           }
224           if (++matches == 5) {
225             cprintf("...\r\n");
226             break;
227           }
228           strcpy(completion, credits.credits[i].nickname);
229           *(completion + len) = '\0';
230           cprintf("%s", completion);
231           textcolor(TC_LIGHT_GREEN);
232           cprintf("%c", *(credits.credits[i].nickname + len));
233           textcolor(TC_LIGHT_GRAY);
234           cprintf("%s\r\n", completion + len + 1);
235         }
236         gotoxy(x, y);
237       }
238     }
239   }
240
241   if (*nickname != '\0' && *nickname != PETSCII_SP) {
242     nickname_len = strlen(nickname);
243     /* go through credits and remove the amount of money or set nickname
244      * to NULL if no such credit could be found */
245     credit = find_credit(nickname);
246     if (credit != NULL) {
247       while ((signed int)credit->credit < ((signed int)price * einheiten)) {
248         if (format_euro(rest, sizeof(rest), credit->credit) == NULL) {
249           cprintf("Preis %d konnte nicht umgerechnet werden\r\n",
250                   credit->credit);
251           exit(1);
252         }
253         cprintf(
254             "\r\n%s hat nicht genug Geld (%s). e) einzahlen a) abbruch \r\n",
255             nickname, rest);
256         c = cgetc();
257         if (c == 'e') {
258           deposit_credit(nickname);
259         } else {
260           return 0;
261         }
262       }
263       /* substract money */
264       credit->credit -= (price * einheiten);
265
266       if (format_euro(rest, sizeof(rest), credit->credit) == NULL) {
267         cprintf("Preis %d konnte nicht umgerechnet werden\r\n", credit->credit);
268         exit(1);
269       }
270
271       textcolor(TC_LIGHT_GREEN);
272       cprintf("\r\nVerbleibendes Guthaben f" uUML "r %s: %s. Dr" uUML
273               "cke RETURN...\r\n",
274               nickname, rest);
275       textcolor(TC_LIGHT_GRAY);
276       cget_return();
277       matches++;
278     } else {
279       textcolor(TC_LIGHT_RED);
280       cprintf("\r\nNickname nicht gefunden in der Guthabenverwaltung! Abbruch, "
281               "dr" uUML "cke RETURN...\r\n");
282       textcolor(TC_LIGHT_GRAY);
283       cget_return();
284       return 0;
285     }
286   } else {
287     /* Ensure that nickname is NULL if it's empty because it's used in print_log
288      */
289     *nickname = '\0';
290   }
291
292   money += price * einheiten;
293   items_sold += einheiten;
294   if (printing == 1)
295     print_log(name, price, einheiten, nickname, rest);
296
297   return einheiten;
298 }
299
300 void buy_stock(BYTE n) {
301   if (n >= status.num_items || status.status[n].item_name == NULL) {
302     cprintf("FEHLER: Diese Einheit existiert nicht.\r\n");
303     cget_return();
304     return;
305   }
306
307   status.status[n].times_sold +=
308       buy(status.status[n].item_name, status.status[n].price);
309 }
310
311 void buy_custom(void) {
312   char name[MAX_ITEM_NAME_LENGTH + 1];
313   int price;
314
315   clrscr();
316   cprintf("\r\nWas soll gekauft werden?\r\n");
317   if (cgetn_input(name, sizeof(name)) == 0)
318     return;
319
320   cprintf("\r\nWie teuer ist \"%s\" (in cents)?\r\n", name);
321
322   price = cget_number(0);
323
324   if (price == 0) {
325     cprintf("Kauf abgebrochen, dr" uUML "cke RETURN...\r\n");
326     cget_return();
327     return;
328   }
329
330   buy(name, price);
331 }
332
333 void set_time_interactive(void) {
334   BYTE part[3] = {'0', '0', '\0'};
335   BYTE tp1, tp2, tp3;
336   char *time_input, *time;
337   cprintf("Gib die aktuelle Uhrzeit ein (Format HHMMSS):\r\n");
338   time_input = get_input();
339   part[0] = time_input[0];
340   part[1] = time_input[1];
341   tp1 = atoi(part);
342   part[0] = time_input[2];
343   part[1] = time_input[3];
344   tp2 = atoi(part);
345   part[0] = time_input[4];
346   part[1] = time_input[5];
347   tp3 = atoi(part);
348   set_time(tp1, tp2, tp3);
349
350   time = get_time();
351   cprintf("\r\nZeit gesetzt: %s\r\n", time);
352 }
353
354 int main(void) {
355   char *c;
356   char *time;
357
358   init_globals();
359
360   videomode(VIDEOMODE_80x25);
361
362   /* clock CPU at double the speed (a whopping 2 Mhz!) */
363   fast();
364
365   /* Manipulate the VDC with IRQs turned off.
366    * KERNALs default IRQ handler will also try to read the VDC status
367    * register, which could interfere with our code trying to read it.
368    */
369   SEI();
370   vdc_patch_charset();
371   CLI();
372
373   clrscr();
374
375   /* Allocate logging buffer memory */
376   init_log();
377
378   /* Set time initially, c128 doesn't know it */
379   set_time_interactive();
380
381   /* disable interrupt driven VIC screen editor */
382   POKE(0xD8, 255);
383
384   /* Load configuration */
385   load_config();
386
387   /* Load items (= drinks) */
388   load_items();
389   /* Load credits */
390   load_credits();
391
392   time = get_time();
393   sprintf(print_buffer, "%c----------------------------------------------------"
394                         "----------------------------\r",
395           17);
396   print_the_buffer();
397   sprintf(print_buffer, "%cC128-Kasse Version " GV "\r", 17);
398   print_the_buffer();
399
400   sprintf(print_buffer,
401           "%cKasse gestartet um %s. Nutze logfile log-%u, offset %d.\r", 17,
402           time, log_num, log_heap_offset);
403   print_the_buffer();
404
405   print_header();
406
407   while (1) {
408     print_screen();
409     c = get_input();
410     /* ...display dialogs eventually */
411     if (*c >= PETSCII_0 && *c <= PETSCII_9) {
412       /* if the input starts with a digit, we will interpret it as a number
413        * for the item to be sold */
414       buy_stock(atoi(c));
415     } else if (*c == 'f') {
416       buy_custom();
417     } else if (*c == 's') {
418       cprintf("\r\nsaving items.. ");
419       save_items();
420       cprintf("ok\r\nsaving credits.. ");
421       save_credits();
422       cprintf("ok\r\nflushing log.. ");
423       log_flush();
424       cprintf("ok\r\nStatefile/Creditfile/Log gesichert, dr" uUML
425               "cke RETURN...\r\n");
426       cget_return();
427     } else if (*c == 'g') {
428       credit_manager();
429     } else if (*c == 'z') {
430       set_time_interactive();
431     } else if (*c == 'q')
432       break;
433   }
434   clrscr();
435   cprintf("\r\nBYEBYE\r\n");
436
437   return 0;
438 }