]> git.sur5r.net Git - c128-kasse/blob - src/kasse.c
Properly replace EURSYM for printer output (#43)
[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[EUR_FORMAT_MINLEN + 1];
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 profit[EUR_FORMAT_MINLEN + 1];
45   clrscr();
46   if (format_euro(profit, sizeof(profit), money) == NULL) {
47     cprintf("Einnahme %ld konnte nicht umgerechnet werden\r\n", money);
48     profit[0] = '\0';
49   }
50   textcolor(TC_CYAN);
51   /* fill whole line with cyan, so color bits are set up for the clock */
52   cprintf("%-80s", "C128-Kasse (phil_fry, sECuRE, sur5r, mxf) " GV);
53   textcolor(TC_LIGHT_GRAY);
54   cprintf("\r\n\r\n"
55           "Ertrag: %s (%ld Artikel); Drucken: %s\r\n",
56           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, int32_t item_price, int16_t einheiten,
113                       char *nickname, char *rest) {
114   char *time = get_time();
115   char *ptr = NULL;
116   uint8_t n;
117   char price[EUR_FORMAT_MINLEN + 1];
118   if (format_euro(price, sizeof(price), item_price) == NULL) {
119     cprintf("Preis %d konnte nicht umgerechnet werden\r\n", item_price);
120     exit(1);
121   }
122
123   /* clang-format off */
124   n = snprintf(print_buffer, sizeof(print_buffer),
125         /* enable lower case letters -- 1 */
126         "%c"
127         /*  Transaction-ID (Anzahl verkaufter Einträge, inklusive des zu druckenden!)
128             -- 6-stellig */
129         "[%3u] "
130         /* Uhrzeit -- 8-stellig + 3 */
131         "%8s - "
132         /*  Eintragname (= Getränk) -- 9-stellig + 3 */
133         "%-" xstr(MAX_ITEM_NAME_LENGTH) "s - "
134         /*  Preis (in Cents) -- 8-stellig  + 3 */
135         "%" xstr(EUR_FORMAT_MINLEN) "s - "
136         /*  restguthaben (8-stellig) + 3 */
137         "%" xstr(EUR_FORMAT_MINLEN) "s - "
138         /*  Anzahl -- 2-stellig + 3 */
139         "%2d - "
140         /*  Nickname (falls es vom Guthaben abgezogen wird) -- 10-stellig + 4 */
141         "an %" xstr(NICKNAME_MAX_LEN)"s\r",
142         17, status.transaction_id, time, name, price, rest, einheiten,
143         (*nickname != '\0' ? nickname : "Unbekannt"));
144   /* clang-format on */
145
146   /* TODO: teach the EUR sign to the printer.
147    * Until then, we just overwrite it with "E" */
148   ptr = print_buffer;
149   while (ptr = strchr(ptr, EURSYM[0])) {
150     *ptr = 'E';
151   }
152
153   if (n > sizeof(print_buffer)) {
154     cprintf("\r\nprint_log(): print_buffer overflowed!\r\n"
155             "Wanted to write %d bytes\r\n%s\r\n",
156             n, print_buffer);
157     exit(1);
158   }
159
160   status.transaction_id++;
161   print_the_buffer();
162 }
163
164 /* dialog which is called for each bought item */
165 static signed int buy(char *name, int32_t price) {
166   BYTE matches = 0;
167   BYTE c, nickname_len;
168   int16_t einheiten;
169   char nickname[NICKNAME_MAX_LEN + 1];
170   char rest[EUR_FORMAT_MINLEN + 1];
171   struct credits_t *credit;
172
173   clrscr();
174   cprintf("Wieviel Einheiten \"%s\"? [1] \r\n", name);
175
176   einheiten = cget_number(1);
177
178   if (einheiten > 100 || einheiten < -100 || einheiten == 0) {
179     cprintf("\r\nEinheit nicht in [-100, 100] oder 0, Abbruch, dr" uUML "cke "
180             "RETURN...\r\n");
181     cget_return();
182     return 1;
183   }
184
185   cprintf("\r\nAuf ein Guthaben kaufen? Wenn ja, Nickname eingeben:\r\n");
186   nickname_len = cget_nickname(nickname, sizeof(nickname));
187
188   if (nickname_len && *nickname != '\0' && *nickname != PETSCII_SP) {
189     /* go through credits and remove the amount of money or set nickname
190      * to NULL if no such credit could be found */
191     credit = find_credit(nickname);
192     if (credit != NULL) {
193       while ((int32_t)credit->credit < (price * einheiten)) {
194         if (format_euro(rest, sizeof(rest), credit->credit) == NULL) {
195           cprintf("Preis %d konnte nicht umgerechnet werden\r\n",
196                   credit->credit);
197           exit(1);
198         }
199         cprintf(
200             "\r\n%s hat nicht genug Geld (%s). e) einzahlen a) abbruch \r\n",
201             nickname, rest);
202         c = cgetc();
203         if (c == 'e') {
204           deposit_credit(nickname);
205         } else {
206           return 0;
207         }
208       }
209       /* substract money */
210       credit->credit -= (price * einheiten);
211
212       if (format_euro(rest, sizeof(rest), credit->credit) == NULL) {
213         cprintf("Preis %d konnte nicht umgerechnet werden\r\n", credit->credit);
214         exit(1);
215       }
216
217       textcolor(TC_LIGHT_GREEN);
218       cprintf("\r\nVerbleibendes Guthaben f" uUML "r %s: %s. Dr" uUML
219               "cke RETURN...\r\n",
220               nickname, rest);
221       textcolor(TC_LIGHT_GRAY);
222       cget_return();
223       matches++;
224     } else {
225       textcolor(TC_LIGHT_RED);
226       cprintf("\r\nNickname nicht gefunden in der Guthabenverwaltung! Abbruch, "
227               "dr" uUML "cke RETURN...\r\n");
228       textcolor(TC_LIGHT_GRAY);
229       cget_return();
230       return 0;
231     }
232   }
233
234   money += price * einheiten;
235   items_sold += einheiten;
236   if (printing == 1)
237     print_log(name, price, einheiten, nickname, rest);
238
239   return einheiten;
240 }
241
242 void buy_stock(BYTE n) {
243   if (n >= status.num_items || status.status[n].item_name == NULL) {
244     cprintf("FEHLER: Diese Einheit existiert nicht.\r\n");
245     cget_return();
246     return;
247   }
248
249   status.status[n].times_sold +=
250       buy(status.status[n].item_name, status.status[n].price);
251 }
252
253 void buy_custom(void) {
254   char name[MAX_ITEM_NAME_LENGTH + 1];
255   int price;
256
257   clrscr();
258   cprintf("\r\nWas soll gekauft werden?\r\n");
259   if (cgetn_input(name, sizeof(name)) == 0)
260     return;
261
262   cprintf("\r\nWie teuer ist \"%s\" (in cents)?\r\n", name);
263
264   price = cget_number(0);
265
266   if (price == 0) {
267     cprintf("Kauf abgebrochen, dr" uUML "cke RETURN...\r\n");
268     cget_return();
269     return;
270   }
271
272   buy(name, price);
273 }
274
275 void set_time_interactive(void) {
276   char part[3] = {'\0', '\0', '\0'};
277   uint8_t day, tp1, tp2, tp3;
278   char *time_input, *time;
279   cprintf("Gib den aktuellen Tag des Events und Uhrzeit ein\r\n"
280           "Format DHHMMSS, 0-indexiert, z.B. 0174259 für \"erster Tag um "
281           "17:42:59\":\r\n");
282   time_input = get_input();
283   part[0] = time_input[0];
284   day = atoi(part);
285   part[0] = time_input[1];
286   part[1] = time_input[2];
287   tp1 = atoi(part);
288   part[0] = time_input[3];
289   part[1] = time_input[4];
290   tp2 = atoi(part);
291   part[0] = time_input[5];
292   part[1] = time_input[6];
293   tp3 = atoi(part);
294   set_time(day, tp1, tp2, tp3);
295
296   time = get_time();
297   cprintf("\r\nZeit gesetzt: %s\r\n", time);
298 }
299
300 int main(void) {
301   char *c;
302   char *time;
303
304   printing = 1;
305   /* initialize daytime global, start the CIA TOD */
306   set_time(0, 0, 0, 0);
307   kasse_menu = MENU_UNDEFINED;
308
309   videomode(VIDEOMODE_80x25);
310
311   /* clock CPU at double the speed (a whopping 2 Mhz!) */
312   fast();
313
314   /* Manipulate the VDC with IRQs turned off.
315    * KERNALs default IRQ handler will also try to read the VDC status
316    * register, which could interfere with our code trying to read it.
317    */
318   SEI();
319   vdc_patch_charset();
320   CLI();
321
322   clrscr();
323
324   install_daytime_irq();
325
326   /* Allocate logging buffer memory */
327   init_log();
328
329   /* Set time initially, c128 doesn't know it */
330   set_time_interactive();
331
332   /* disable interrupt driven VIC screen editor */
333   POKE(0xD8, 255);
334
335   /* Load configuration */
336   load_config();
337
338   /* Load items (= drinks) */
339   load_items();
340   /* Load credits */
341   load_credits();
342
343   time = get_time();
344   sprintf(print_buffer, "%c----------------------------------------------------"
345                         "----------------------------\r",
346           17);
347   print_the_buffer();
348   sprintf(print_buffer, "%cC128-Kasse Version " GV "\r", 17);
349   print_the_buffer();
350
351   sprintf(print_buffer,
352           "%cKasse gestartet um %s. Nutze logfile log-%u, offset %d.\r", 17,
353           time, log_num, log_heap_offset);
354   print_the_buffer();
355
356   print_header();
357
358   while (1) {
359     print_screen();
360     kasse_menu = MENU_MAIN;
361     c = get_input();
362     kasse_menu = MENU_UNDEFINED;
363     /* ...display dialogs eventually */
364     if (*c >= PETSCII_0 && *c <= PETSCII_9) {
365       /* if the input starts with a digit, we will interpret it as a number
366        * for the item to be sold */
367       buy_stock(atoi(c));
368     } else if (*c == 'f') {
369       buy_custom();
370     } else if (*c == 's') {
371       cprintf("\r\nsaving items.. ");
372       save_items();
373       cprintf("ok\r\nsaving credits.. ");
374       save_credits();
375       cprintf("ok\r\nflushing log.. ");
376       log_flush();
377       cprintf("ok\r\nStatefile/Creditfile/Log gesichert, dr" uUML
378               "cke RETURN...\r\n");
379       cget_return();
380     } else if (*c == 'g') {
381       credit_manager();
382     } else if (*c == 'z') {
383       set_time_interactive();
384     } else if (*c == 'q')
385       break;
386   }
387   clrscr();
388   cprintf("\r\nBYEBYE\r\n");
389
390   return 0;
391 }