]> git.sur5r.net Git - c128-kasse/blob - src/kasse.c
Add git version string
[c128-kasse] / src / kasse.c
1 /* 
2  * RGB2R-C128-Kassenprogramm
3  * (c) 2007-2008 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
14 #include "general.h"
15 #include "config.h"
16 #include "kasse.h"
17 #include "credit_manager.h"
18 #include "c128time.h"
19 // drucker 4 oder 5
20 // graphic 4,0,10
21
22 /* NOTE: undocumented function which scratches files
23    We need to use this function because linking unistd.h
24    makes our program break at runtime.
25  */
26 unsigned char __fastcall__ _sysremove(const char *name);
27
28 static void sane_exit() {
29         save_items();
30         save_credits();
31         exit(1);
32 }
33
34 /* Hauptbildschirm ausgeben */
35 static void print_screen() {
36         BYTE i = 0;
37         char *time = get_time();
38         char profit[10];
39         clrscr();
40         if (format_euro(profit, 10, money) == NULL) {
41                 cprintf("Einnahme %ld konnte nicht umgerechnet werden\r\n", money);
42                 exit(1);
43         }
44         cprintf("C128-Kassenprogramm (phil_fry, sECuRE, sur5r) v:" GV "\r\
45 \r\nUhrzeit: %s (wird nicht aktualisiert)\r\
46 Eingenommen: %s, Verkauft: %ld Flaschen, Drucken: %s\r\n\r\n", 
47         time, profit, items_sold, (printing == 1 ? "ein" : "aus"));
48         for (; i < status.num_items; ++i)
49                 cprintf("Eintrag %x: %s (%d Cents, %d mal verkauft)\r\n",
50                         i, status.status[i].item_name, status.status[i].price, status.status[i].times_sold);
51         cprintf("\r\nBefehle: s) Daten sichern d) Drucken umschalten\r\
52 g) Guthabenverwaltung     z) Zeit setzen\r\
53 f) Freitext verkaufen     q) Beenden\r\n");
54 }
55
56 static void log_file(const char *s) {
57         /* A log-entry has usually 50 bytes, so we take 64 bytes.
58            Because files are wrapped (log.0, log.1, ...) every 100
59            lines, we don't need more than 100 * 64 bytes. */
60         char *buffer = malloc(sizeof(char) * 64 * 100);
61         char filename[8];
62         int read = 0;
63         unsigned int c;
64         if (buffer == NULL) {
65                 cprintf("No memory available\n");
66                 }
67         buffer[0] = '\0';
68         if (((++log_lines_written) % 100) == 0)
69                 log_num++;
70         sprintf(filename, "log-%d", log_num);
71         /* Don't read log if there were no lines written before */
72         if (log_lines_written != 1) {
73                 if ((c = cbm_open((BYTE)8, (BYTE)8, (BYTE)0, filename)) != 0) {
74                         c128_perror(c, "cbm_open(log)");
75                         sane_exit();
76                 }
77                 read = cbm_read((BYTE)8, buffer, sizeof(char) * 64 * 100);
78                 cbm_close((BYTE)8);
79                 _sysremove(filename);
80         }
81         if ((c = cbm_open((BYTE)8, (BYTE)8, (BYTE)1, filename)) != 0) {
82                 c128_perror(c, "cbm_open(log)");
83                 sane_exit();
84         }
85         if (read < 0) {
86                 cprintf("Could not read existing logfile (read returned %d)\n", read);
87                 sane_exit();
88         }
89         strcpy(buffer+read, s);
90         c = cbm_write((BYTE)8, buffer, read+strlen(s));
91         if (c != (read+strlen(s))) {
92                 cprintf("Could not save logfile (wrote %d bytes, wanted %d bytes), please make sure the floppy is not full!\n", c, (read+strlen(s)));
93                 sane_exit();
94         }
95         cbm_close((BYTE)8);
96         free(buffer);
97 }
98
99 static char retry_or_quit() {
100         char *c;
101         do {
102                 cprintf("\r\nr)etry or q)uit?\r\n");
103                 c = get_input();
104         } while ((*c != 'r') && (*c != 'q'));
105         return *c;
106 }
107
108 /* Prints a line and logs it to file */
109 static void print_log(char *name, int item_price, int einheiten, char *nickname, char *rest) {
110         char *time = get_time();
111         char price[10];
112         /* Format: 
113            Transaction-ID (Anzahl verkaufter Einträge, inklusive des zu druckenden!) -- 6-stellig
114            Uhrzeit -- 8-stellig
115            Eintragname (= Getränk) -- 9-stellig
116            Preis (in Cents) -- 7-stellig
117            Anzahl -- 2-stellig
118            Nickname (falls es vom Guthaben abgezogen wird) -- 10-stellig
119            restguthaben (9-stellig)
120
121            + 7 leerzeichen
122            --> 48 zeichen
123            */
124         if (format_euro(price, 10, item_price) == NULL) {
125                 cprintf("Preis %d konnte nicht umgerechnet werden\r\n", item_price);
126                 exit(1);
127         }
128
129         sprintf(print_buffer, "%c[%lu] %s - %-9s - %s - r %s - %d - an %s\r",  17,
130                         items_sold, time, name, price, rest,
131                         einheiten, (*nickname != '\0' ? nickname : "Unbekannt"));
132         print_the_buffer();
133 }
134
135 void print_the_buffer() {
136         BYTE c;
137 RETRY:
138         c = cbm_open((BYTE)4, (BYTE)4, (BYTE)0, NULL);
139         if (c != 0) {
140                 c128_perror(c, "cbm_open(printer)");
141                 if (retry_or_quit() == 'q')
142                         sane_exit();
143
144                 goto RETRY;
145         }
146         c = cbm_write((BYTE)4, print_buffer, strlen(print_buffer));
147         if (c != strlen(print_buffer)) {
148                 c128_perror(c, "write(printer)");
149                 if (retry_or_quit() == 'q') {
150                         save_items();
151                         save_credits();
152                         exit(1);
153                 }
154                 goto RETRY;
155         }
156         cbm_close((BYTE)4);
157         log_file(print_buffer);
158 }
159
160 /* dialog which is called for each bought item */
161 static signed int buy(char *name, unsigned int price) {
162         int negative = 1;
163         char entered[5] = {'1', 0, 0, 0, 0};
164         BYTE i = 0, matches = 0;
165         BYTE c, nickname_len;
166         int einheiten;
167         char *input;
168         char nickname[11];
169         char rest[9];
170         struct credits_t *credit;
171
172         memset(rest, ' ', sizeof(rest));
173         rest[8] = '\0';
174
175         cprintf("Wieviel Einheiten \"%s\"? [1] \r\n", name);
176         while (1) {
177                 c = getchar();
178                 if (c == 13)
179                         break;
180                 else if (c == 27) {
181                         cprintf("Kauf abgebrochen, druecke RETURN...\r\n");
182                         get_input();
183                         return 1;
184                 } else if (c == '-' && i == 0)
185                         negative = -1;
186                 else if (c > 47 && c < 58)
187                         entered[i++] = c;
188         }
189         einheiten = atoi(entered) * negative;
190         
191         toggle_videomode();
192         cprintf("%dx %s fuer ", einheiten, name);
193         toggle_videomode();
194         
195         cprintf("\r\nAuf ein Guthaben kaufen? Wenn ja, Nickname eingeben:\r\n");
196         input = get_input();
197         strncpy(nickname, input, 11);
198         if (*nickname != '\0') {
199                 toggle_videomode();
200                 cprintf("%s\r\n", nickname);
201                 toggle_videomode();
202         }
203
204         if (nickname != NULL && *nickname != '\0' && *nickname != 32) {
205                 nickname_len = strlen(nickname);
206                 /* go through credits and remove the amount of money or set nickname
207                  * to NULL if no such credit could be found */
208                 credit = find_credit(nickname);
209                 if (credit != NULL) {
210                         if ((signed int)credit->credit < ((signed int)price * einheiten)) {
211                                 cprintf("Sorry, %s hat nicht genug Geld :-(\r\n", nickname);
212                                 get_input();
213                                 return 0;
214                         }
215                         /* substract money */
216                         credit->credit -= (price * einheiten);
217
218                         if (format_euro(rest, 10, credit->credit) == NULL) {
219                                 cprintf("Preis %d konnte nicht umgerechnet werden\r\n", credit->credit);
220                                 exit(1);
221                         }
222
223                         cprintf("\r\nVerbleibendes Guthaben fuer %s: %s. Druecke RETURN...\r\n",
224                                 nickname, rest);
225                         toggle_videomode();
226                         cprintf("\r\nDein verbleibendes Guthaben betraegt %s.\r\n", rest);
227                         toggle_videomode();
228                         get_input();
229                         matches++;
230                 } else {
231                         cprintf("\r\nNickname nicht gefunden in der Guthabenverwaltung! Abbruch, druecke RETURN...\r\n");
232                         get_input();
233                         return 0;
234                 }
235         } else {
236                 /* Ensure that nickname is NULL if it's empty because it's used in print_log */
237                 *nickname = '\0';
238         }
239         
240         money += price * einheiten;
241         items_sold += einheiten;
242         if (printing == 1)
243                 print_log(name, price, einheiten, nickname, rest);
244
245         return einheiten;
246 }
247
248 void buy_stock(BYTE n) {
249         if (n >= status.num_items || status.status[n].item_name == NULL) {
250                 cprintf("FEHLER: Diese Einheit existiert nicht.\r\n");
251                 get_input();
252                 return;
253         }
254
255         status.status[n].times_sold += buy(status.status[n].item_name, status.status[n].price);
256 }
257
258 void buy_custom() {
259         BYTE c = 0, i = 0;
260         int negative = 1;
261         char entered[5] = {'1', 0, 0, 0, 0};
262         char *input, name[20];
263         int price;
264
265         clrscr();
266         memset(name, '\0', 20);
267         cprintf("\r\nWas soll gekauft werden?\r\n");
268         input = get_input();
269         strncpy(name, input, 20);
270         if (*name == '\0')
271                 return;
272
273         cprintf("\r\nWie teuer ist \"%s\" (in cents)?\r\n", name);
274         while (1) {
275                 c = getchar();
276                 if (c == 13)
277                         break;
278                 else if (c == 27) {
279                         cprintf("Kauf abgebrochen, druecke RETURN...\r\n");
280                         get_input();
281                         return;
282                 } else if (c == '-' && i == 0)
283                         negative = -1;
284                 else if (c > 47 && c < 58)
285                         entered[i++] = c;
286         }
287         price = atoi(entered) * negative;
288
289         cprintf("\r\n");
290
291         buy(name, price);
292 }
293
294 void set_time_interactive() {
295         BYTE part[3] = {'0', '0', '\0'};
296         BYTE tp1, tp2, tp3;
297         char *time_input, *time;
298         cprintf("Gib die aktuelle Uhrzeit ein (Format HHMMSS):\r\n");
299         time_input = get_input();
300         part[0] = time_input[0];
301         part[1] = time_input[1];
302         tp1 = atoi(part);
303         part[0] = time_input[2];
304         part[1] = time_input[3];
305         tp2 = atoi(part);
306         part[0] = time_input[4];
307         part[1] = time_input[5];
308         tp3 = atoi(part);
309         set_time(tp1, tp2, tp3);
310
311         time = get_time();
312         cprintf("\r\nZeit gesetzt: %s\r\n", time);
313 }
314
315 int main() {
316         char *c;
317
318         if (VIDEOMODE == 40)
319                 toggle_videomode();
320         clrscr();
321         /* Set time initially, c128 doesn't know it */
322         set_time_interactive();
323
324         POKE(216, 255);
325
326         /* Load configuration */
327         load_config();
328         cprintf("got %d logfiles\r\n", log_num);
329
330         /* Load items (= drinks) */
331         load_items();
332         /* Load credits */
333         load_credits();
334         while (1) {
335                 print_screen();
336                 c = get_input();
337                 /* ...display dialogs eventually */
338                 if (*c > 47 && *c < 58) {
339                         buy_stock((*c) - 48);
340                         toggle_videomode();
341                         clrscr();
342                         toggle_videomode();
343                 } else if (*c == 'f') {
344                         buy_custom();
345                         toggle_videomode();
346                         clrscr();
347                         toggle_videomode();
348                 } else if (*c == 's') {
349                         save_items();
350                         save_credits();
351                         cprintf("Statefile/Creditfile gesichert, druecke RETURN...\r\n");
352                         get_input();
353                 } else if (*c == 'd') {
354                         /* enable/disable printing */
355                         printing = (printing == 1 ? 0 : 1);
356                         cprintf("Drucken ist nun %s, druecke RETURN...\r\n", 
357                                 (printing == 1 ? "eingeschaltet" : "ausgeschaltet"));
358                         get_input();
359                 } else if (*c == 'g') {
360                         credit_manager();
361                 } else if (*c == 'z') {
362                         set_time_interactive();
363                 } else if (*c == 'q')
364                         break;
365         }
366         cprintf("BYEBYE\r\n");
367
368         return 0;
369 }