]> git.sur5r.net Git - c128-kasse/blob - src/kasse.c
kasse: use constants where appropriate
[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 #define _IS_KASSE
8 #include <stdio.h>
9 #include <conio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <cbm.h>
13 #include <c128.h>
14 #include <6502.h>
15
16 #include "general.h"
17 #include "config.h"
18 #include "kasse.h"
19 #include "credit_manager.h"
20 #include "c128time.h"
21 #include "print.h"
22 #include "version.h"
23 #include "vdc_patch_charset.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   int negative = 1;
145   char entered[5] = {'1', 0, 0, 0, 0};
146   BYTE i = 0, matches = 0;
147   BYTE c, x, y, nickname_len;
148   int einheiten;
149   char nickname[NICKNAME_MAX_LEN + 1];
150   char rest[10];
151   struct credits_t *credit;
152
153   memset(nickname, '\0', sizeof(nickname));
154   memset(rest, '\0', sizeof(rest));
155
156   clrscr();
157   cprintf("Wieviel Einheiten \"%s\"? [1] \r\n", name);
158   x = wherex();
159   y = wherey();
160   while (1) {
161     /* Buffer-Ende erreicht? */
162     if (i == (sizeof(entered) - 1))
163       break;
164
165     c = cgetc();
166     /* Enter */
167     if (c == PETSCII_CR)
168       break;
169     /* Backspace */
170     if (c == PETSCII_DEL) {
171       if (i == 0)
172         continue;
173       entered[--i] = '\0';
174       cputcxy(x + i, y, ' ');
175       gotoxy(x + i, y);
176       continue;
177     }
178     if (c == PETSCII_ESC) {
179       cprintf("Kauf abgebrochen, dr" uUML "cke RETURN...\r\n");
180       get_input();
181       return 1;
182     }
183     if (c == '-' && i == 0) {
184       negative = -1;
185       cputc(c);
186     } else if (c >= PETSCII_0 && c <= PETSCII_9) {
187       entered[i++] = c;
188       cputc(c);
189     }
190
191     /* Ungültige Eingabe (keine Ziffer), einfach ignorieren */
192   }
193   einheiten = atoi(entered) * negative;
194
195   if (einheiten > 100 || einheiten < -100 || einheiten == 0) {
196     cprintf("\r\nEinheit nicht in [-100, 100] oder 0, Abbruch, dr" uUML "cke "
197             "RETURN...\r\n");
198     cgetc();
199     return 1;
200   }
201
202   cprintf("\r\nAuf ein Guthaben kaufen? Wenn ja, Nickname eingeben:\r\n");
203   {
204     BYTE i;
205     BYTE x;
206     BYTE y;
207     BYTE matches;
208     char *uniquematch;
209     input_terminator_t terminator;
210     while (1) {
211       terminator = get_input_terminated_by(INPUT_TERMINATOR_RETURN |
212                                                INPUT_TERMINATOR_SPACE,
213                                            nickname, sizeof(nickname));
214
215       /* Clear the screen from any previous completions */
216       x = wherex();
217       y = wherey();
218       for (i = 1; i < 7; i++) {
219         /* "Completion:" is longer than NICKNAME_MAX_LEN */
220         cclearxy(0, y + i, strlen("Completion:"));
221       }
222       gotoxy(x, y);
223
224       if (terminator != INPUT_TERMINATOR_SPACE) {
225         break;
226       }
227
228       matches = 0;
229       uniquematch = NULL;
230       for (i = 0; i < credits.num_items; i++) {
231         if (strncmp(nickname, credits.credits[i].nickname, strlen(nickname)) !=
232             0) {
233           continue;
234         }
235         matches++;
236         if (matches > 1) {
237           break;
238         }
239         uniquematch = credits.credits[i].nickname;
240       }
241       if (matches == 1) {
242         /* Display the rest of the nickname */
243         textcolor(TC_LIGHT_GREEN);
244         cprintf("%s", uniquematch + strlen(nickname));
245         textcolor(TC_LIGHT_GRAY);
246         strcat(nickname, uniquematch + strlen(nickname));
247       } else {
248         /* Multiple nicknames match what was entered so far. Abort and
249          * display all matches, then prompt the user again. */
250         char completion[NICKNAME_MAX_LEN + 1];
251         BYTE len = strlen(nickname);
252         x = wherex();
253         y = wherey();
254         cprintf("\r\nCompletion:\r\n");
255         matches = 0;
256         for (i = 0; i < credits.num_items; i++) {
257           if (strncmp(nickname, credits.credits[i].nickname, len) != 0) {
258             continue;
259           }
260           if (++matches == 5) {
261             cprintf("...\r\n");
262             break;
263           }
264           strcpy(completion, credits.credits[i].nickname);
265           *(completion + len) = '\0';
266           cprintf("%s", completion);
267           textcolor(TC_LIGHT_GREEN);
268           cprintf("%c", *(credits.credits[i].nickname + len));
269           textcolor(TC_LIGHT_GRAY);
270           cprintf("%s\r\n", completion + len + 1);
271         }
272         gotoxy(x, y);
273       }
274     }
275   }
276
277   if (*nickname != '\0' && *nickname != PETSCII_SP) {
278     nickname_len = strlen(nickname);
279     /* go through credits and remove the amount of money or set nickname
280      * to NULL if no such credit could be found */
281     credit = find_credit(nickname);
282     if (credit != NULL) {
283       while ((signed int)credit->credit < ((signed int)price * einheiten)) {
284         if (format_euro(rest, sizeof(rest), credit->credit) == NULL) {
285           cprintf("Preis %d konnte nicht umgerechnet werden\r\n",
286                   credit->credit);
287           exit(1);
288         }
289         cprintf(
290             "\r\n%s hat nicht genug Geld (%s). e) einzahlen a) abbruch \r\n",
291             nickname, rest);
292         c = cgetc();
293         if (c == 'e') {
294           deposit_credit(nickname);
295         } else {
296           return 0;
297         }
298       }
299       /* substract money */
300       credit->credit -= (price * einheiten);
301
302       if (format_euro(rest, sizeof(rest), credit->credit) == NULL) {
303         cprintf("Preis %d konnte nicht umgerechnet werden\r\n", credit->credit);
304         exit(1);
305       }
306
307       textcolor(TC_LIGHT_GREEN);
308       cprintf("\r\nVerbleibendes Guthaben f" uUML "r %s: %s. Dr" uUML
309               "cke RETURN...\r\n",
310               nickname, rest);
311       textcolor(TC_LIGHT_GRAY);
312       get_input();
313       matches++;
314     } else {
315       textcolor(TC_LIGHT_RED);
316       cprintf("\r\nNickname nicht gefunden in der Guthabenverwaltung! Abbruch, "
317               "dr" uUML "cke RETURN...\r\n");
318       textcolor(TC_LIGHT_GRAY);
319       get_input();
320       return 0;
321     }
322   } else {
323     /* Ensure that nickname is NULL if it's empty because it's used in print_log
324      */
325     *nickname = '\0';
326   }
327
328   money += price * einheiten;
329   items_sold += einheiten;
330   if (printing == 1)
331     print_log(name, price, einheiten, nickname, rest);
332
333   return einheiten;
334 }
335
336 void buy_stock(BYTE n) {
337   if (n >= status.num_items || status.status[n].item_name == NULL) {
338     cprintf("FEHLER: Diese Einheit existiert nicht.\r\n");
339     get_input();
340     return;
341   }
342
343   status.status[n].times_sold +=
344       buy(status.status[n].item_name, status.status[n].price);
345 }
346
347 void buy_custom(void) {
348   BYTE c = 0, i = 0;
349   int negative = 1;
350   char entered[5] = {'1', 0, 0, 0, 0};
351   char *input, name[20];
352   int price;
353
354   clrscr();
355   memset(name, '\0', sizeof(name));
356   cprintf("\r\nWas soll gekauft werden?\r\n");
357   input = get_input();
358   strncpy(name, input, sizeof(name));
359   if (*name == '\0')
360     return;
361
362   cprintf("\r\nWie teuer ist \"%s\" (in cents)?\r\n", name);
363   while (1) {
364     c = cgetc();
365     if (c == PETSCII_CR)
366       break;
367     cputc(c);
368     if (c == PETSCII_ESC) {
369       cprintf("Kauf abgebrochen, dr" uUML "cke RETURN...\r\n");
370       get_input();
371       return;
372     } else if (c == '-' && i == 0)
373       negative = -1;
374     else if (c >= PETSCII_0 && c <= PETSCII_9)
375       entered[i++] = c;
376   }
377   price = atoi(entered) * negative;
378
379   cprintf("\r\n");
380
381   buy(name, price);
382 }
383
384 void set_time_interactive(void) {
385   BYTE part[3] = {'0', '0', '\0'};
386   BYTE tp1, tp2, tp3;
387   char *time_input, *time;
388   cprintf("Gib die aktuelle Uhrzeit ein (Format HHMMSS):\r\n");
389   time_input = get_input();
390   part[0] = time_input[0];
391   part[1] = time_input[1];
392   tp1 = atoi(part);
393   part[0] = time_input[2];
394   part[1] = time_input[3];
395   tp2 = atoi(part);
396   part[0] = time_input[4];
397   part[1] = time_input[5];
398   tp3 = atoi(part);
399   set_time(tp1, tp2, tp3);
400
401   time = get_time();
402   cprintf("\r\nZeit gesetzt: %s\r\n", time);
403 }
404
405 int main(void) {
406   char *c;
407   char *time;
408
409   videomode(VIDEOMODE_80x25);
410
411   /* clock CPU at double the speed (a whopping 2 Mhz!) */
412   fast();
413
414   /* Manipulate the VDC with IRQs turned off.
415    * KERNALs default IRQ handler will also try to read the VDC status
416    * register, which could interfere with our code trying to read it.
417    */
418   SEI();
419   vdc_patch_charset();
420   CLI();
421
422   clrscr();
423
424   /* Allocate logging buffer memory */
425   init_log();
426
427   /* Set time initially, c128 doesn't know it */
428   set_time_interactive();
429
430   /* disable interrupt driven VIC screen editor */
431   POKE(0xD8, 255);
432
433   /* Load configuration */
434   load_config();
435
436   /* Load items (= drinks) */
437   load_items();
438   /* Load credits */
439   load_credits();
440
441   time = get_time();
442   sprintf(print_buffer, "%c----------------------------------------------------"
443                         "----------------------------\r",
444           17);
445   print_the_buffer();
446   sprintf(print_buffer, "%cC128-Kasse Version " GV "\r", 17);
447   print_the_buffer();
448
449   sprintf(print_buffer,
450           "%cKasse gestartet um %s. Nutze logfile log-%u, offset %d.\r", 17,
451           time, log_num, log_heap_offset);
452   print_the_buffer();
453
454   print_header();
455
456   while (1) {
457     print_screen();
458     c = get_input();
459     /* ...display dialogs eventually */
460     if (*c >= PETSCII_0 && *c <= PETSCII_9) {
461       /* if the input starts with a digit, we will interpret it as a number
462        * for the item to be sold */
463       buy_stock(atoi(c));
464     } else if (*c == 'f') {
465       buy_custom();
466     } else if (*c == 's') {
467       cprintf("\r\nsaving items.. ");
468       save_items();
469       cprintf("ok\r\nsaving credits.. ");
470       save_credits();
471       cprintf("ok\r\nflushing log.. ");
472       log_flush();
473       cprintf("ok\r\nStatefile/Creditfile/Log gesichert, dr" uUML
474               "cke RETURN...\r\n");
475       get_input();
476     } else if (*c == 'g') {
477       credit_manager();
478     } else if (*c == 'z') {
479       set_time_interactive();
480     } else if (*c == 'q')
481       break;
482   }
483   clrscr();
484   cprintf("\r\nBYEBYE\r\n");
485
486   return 0;
487 }