Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MorsGames
GitHub Repository: MorsGames/sm64plus
Path: blob/master/src/goddard/debug_utils.c
7858 views
1
#include <PR/ultratypes.h>
2
#include <stdarg.h>
3
4
#include "debug_utils.h"
5
#include "gd_types.h"
6
#include "macros.h"
7
#include "renderer.h"
8
#include "draw_objects.h"
9
10
// types
11
struct UnkBufThing {
12
/* 0x00 */ s32 size;
13
/* 0x04 */ char name[0x40];
14
}; /* sizeof = 0x44 */
15
16
// data
17
static s32 sNumRoutinesInStack = 0; // @ 801A8280
18
static s32 sTimerGadgetColours[7] = {
19
COLOUR_RED,
20
COLOUR_WHITE,
21
COLOUR_GREEN,
22
COLOUR_BLUE,
23
COLOUR_GRAY,
24
COLOUR_YELLOW,
25
COLOUR_PINK
26
};
27
static s32 sNumActiveMemTrackers = 0; // @ 801A82A0
28
static u32 sPrimarySeed = 0x12345678; // @ 801A82A4
29
static u32 sSecondarySeed = 0x58374895; // @ 801A82A8
30
31
// bss
32
u8 *gGdStreamBuffer; // @ 801BA190
33
static const char *sRoutineNames[64]; // @ 801BA198
34
static s32 sTimingActive; // @ 801BA298
35
static struct GdTimer sTimers[GD_NUM_TIMERS]; // @ 801BA2A0
36
static struct MemTracker sMemTrackers[GD_NUM_MEM_TRACKERS]; // @ 801BA720
37
static struct MemTracker *sActiveMemTrackers[16]; // @ 801BA920
38
39
/*
40
* Memtrackers
41
*
42
* These are used to monitor how much heap memory is being used by certain
43
* operations.
44
* To create a memtracker, call new_memtracker with a unique name.
45
* To record the amount of memory used by a certain allocation, call
46
* start_memtracker before allocating memory, and call stop_memtracker after
47
* allocating memory.
48
* The memtracker keeps track of the memory allocated between a single
49
* start_memtracker/stop_memtracker pair as well as the total memory allocated
50
* of all start_memtracker/stop_memtracker pairs.
51
*/
52
53
/**
54
* Creates a new memtracker with the specified name
55
*/
56
struct MemTracker *new_memtracker(const char *name) {
57
s32 i;
58
struct MemTracker *tracker = NULL;
59
60
for (i = 0; i < ARRAY_COUNT(sMemTrackers); i++) {
61
if (sMemTrackers[i].name == NULL) {
62
sMemTrackers[i].name = name;
63
tracker = &sMemTrackers[i];
64
break;
65
}
66
}
67
68
if (tracker != NULL) {
69
tracker->total = 0.0f;
70
}
71
72
return tracker;
73
}
74
75
/**
76
* Returns the memtracker with the specified name, or NULL if it
77
* does not exist
78
*/
79
struct MemTracker *get_memtracker(const char *name) {
80
s32 i;
81
82
for (i = 0; i < ARRAY_COUNT(sMemTrackers); i++) {
83
if (sMemTrackers[i].name != NULL) {
84
if (gd_str_not_equal(sMemTrackers[i].name, name) == FALSE) {
85
return &sMemTrackers[i];
86
}
87
}
88
}
89
90
return NULL;
91
}
92
93
/**
94
* Records the amount of heap usage before allocating memory.
95
*/
96
struct MemTracker *start_memtracker(const char *name) {
97
struct MemTracker *tracker = get_memtracker(name);
98
99
// Create one if it doesn't exist
100
if (tracker == NULL) {
101
tracker = new_memtracker(name);
102
if (tracker == NULL) {
103
fatal_printf("Unable to make memtracker '%s'", name);
104
}
105
}
106
107
tracker->begin = (f32) get_alloc_mem_amt();
108
if (sNumActiveMemTrackers >= ARRAY_COUNT(sActiveMemTrackers)) {
109
fatal_printf("too many memtracker calls");
110
}
111
112
sActiveMemTrackers[sNumActiveMemTrackers++] = tracker;
113
114
return tracker;
115
}
116
117
/* @ 23ABE0 -> 23AC28; not called; orig name: Unknown8018C410 */
118
void print_most_recent_memtracker_name(void) {
119
gd_printf("%s\n", sActiveMemTrackers[sNumActiveMemTrackers - 1]->name);
120
}
121
122
/**
123
* Records the amount of heap usage after allocating memory.
124
*/
125
u32 stop_memtracker(const char *name) {
126
struct MemTracker *tracker;
127
128
if (sNumActiveMemTrackers-- < 0) {
129
fatal_printf("bad mem tracker count");
130
}
131
132
tracker = get_memtracker(name);
133
if (tracker == NULL) {
134
fatal_printf("memtracker '%s' not found", name);
135
}
136
137
tracker->end = get_alloc_mem_amt();
138
tracker->total += (tracker->end - tracker->begin);
139
140
return (u32) tracker->total;
141
}
142
143
/**
144
* Destroys all memtrackers
145
*/
146
void remove_all_memtrackers(void) {
147
s32 i;
148
149
for (i = 0; i < ARRAY_COUNT(sMemTrackers); i++) {
150
sMemTrackers[i].name = NULL;
151
sMemTrackers[i].begin = 0.0f;
152
sMemTrackers[i].end = 0.0f;
153
sMemTrackers[i].total = 0.0f;
154
}
155
156
#ifdef AVOID_UB
157
sNumActiveMemTrackers = 0;
158
#endif
159
}
160
161
/**
162
* Returns a memtracker by index rather than name
163
*/
164
struct MemTracker *get_memtracker_by_index(s32 index) {
165
return &sMemTrackers[index];
166
}
167
168
/**
169
* Prints the total memory allocated for each memtracker
170
*/
171
void print_all_memtrackers(void) {
172
s32 i;
173
174
for (i = 0; i < ARRAY_COUNT(sMemTrackers); i++) {
175
if (sMemTrackers[i].name != NULL) {
176
gd_printf("'%s' = %dk\n", sMemTrackers[i].name, (s32)(sMemTrackers[i].total / 1024.0f));
177
}
178
}
179
}
180
181
/*
182
* Timers
183
*
184
* These are used to profile the code by measuring the time it takes to perform
185
* operations.
186
* To record elapsed time, call start_timer, perform some operations, then call stop_timer.
187
* You can also use restart_timer/split_timer instead of start_timer/stop_timer
188
* to keep a running total.
189
*/
190
191
/* 23AEFC -> 23AFB0; orig name: func_8018C72C */
192
void print_all_timers(void) {
193
s32 i;
194
195
gd_printf("\nTimers:\n");
196
for (i = 0; i < ARRAY_COUNT(sTimers); i++) {
197
if (sTimers[i].name != NULL) {
198
gd_printf("'%s' = %f (%d)\n", sTimers[i].name, sTimers[i].scaledTotal,
199
sTimers[i].resetCount);
200
}
201
}
202
}
203
204
/* 23AFB0 -> 23AFC8; orig name: func_8018C7E0 */
205
void deactivate_timing(void) {
206
sTimingActive = FALSE;
207
}
208
209
/* 23AFC8 -> 23AFE4; orig name: func_8018C7F8 */
210
void activate_timing(void) {
211
sTimingActive = TRUE;
212
}
213
214
/**
215
* Destroys all timers
216
*/
217
void remove_all_timers(void) {
218
s32 i;
219
220
for (i = 0; i < ARRAY_COUNT(sTimers); i++) {
221
sTimers[i].name = NULL;
222
sTimers[i].total = 0;
223
sTimers[i].unused0C = 0.0f;
224
sTimers[i].scaledTotal = 0.0f;
225
sTimers[i].prevScaledTotal = 0.0f;
226
sTimers[i].gadgetColourNum = sTimerGadgetColours[(u32) i % 7];
227
sTimers[i].resetCount = 0;
228
}
229
activate_timing();
230
}
231
232
/**
233
* Creates a new timer with the specified name
234
*/
235
static struct GdTimer *new_timer(const char *name) {
236
s32 i;
237
struct GdTimer *timer = NULL;
238
239
for (i = 0; i < ARRAY_COUNT(sTimers); i++) {
240
if (sTimers[i].name == NULL) {
241
sTimers[i].name = name;
242
timer = &sTimers[i];
243
break;
244
}
245
}
246
247
return timer;
248
}
249
250
/**
251
* Returns the timer with the specified name, or NULL if it does not exist.
252
*/
253
struct GdTimer *get_timer(const char *timerName) {
254
s32 i;
255
256
for (i = 0; i < ARRAY_COUNT(sTimers); i++) {
257
if (sTimers[i].name != NULL) {
258
if (gd_str_not_equal(sTimers[i].name, timerName) == FALSE) {
259
return &sTimers[i];
260
}
261
}
262
}
263
264
return NULL;
265
}
266
267
/**
268
* Returns the timer with the specified name, or aborts the program if it does
269
* not exist.
270
*/
271
static struct GdTimer *get_timer_checked(const char *timerName) {
272
struct GdTimer *timer;
273
274
timer = get_timer(timerName);
275
if (timer == NULL) {
276
fatal_printf("Timer '%s' not found", timerName);
277
}
278
279
return timer;
280
}
281
282
/**
283
* Returns a timer by index rather than name
284
*/
285
struct GdTimer *get_timernum(s32 index) {
286
if (index >= ARRAY_COUNT(sTimers)) {
287
fatal_printf("get_timernum(): Timer number %d out of range (MAX %d)", index, ARRAY_COUNT(sTimers));
288
}
289
290
return &sTimers[index];
291
}
292
293
/* 23B350 -> 23B42C; orig name: func_8018CB80 */
294
void split_timer_ptr(struct GdTimer *timer) {
295
if (!sTimingActive) {
296
return;
297
}
298
299
timer->end = gd_get_ostime();
300
timer->total += timer->end - timer->start;
301
302
if (timer->total < 0) {
303
timer->total = 0;
304
}
305
306
timer->scaledTotal = ((f32) timer->total) / get_time_scale();
307
timer->start = timer->end;
308
}
309
310
/* 23B42C -> 23B49C; not called; orig name: Unknown8018CC5C */
311
void split_all_timers(void) {
312
s32 i;
313
struct GdTimer *timer;
314
315
for (i = 0; i < ARRAY_COUNT(sTimers); i++) {
316
timer = get_timernum(i);
317
if (timer->name != NULL) {
318
split_timer_ptr(timer);
319
}
320
}
321
}
322
323
/**
324
* Unused - records the start time for all timers
325
*/
326
void start_all_timers(void) {
327
s32 i;
328
struct GdTimer *timer;
329
330
if (!sTimingActive) {
331
return;
332
}
333
334
for (i = 0; i < ARRAY_COUNT(sTimers); i++) {
335
timer = get_timernum(i);
336
337
if (timer->name != NULL) {
338
timer->start = gd_get_ostime();
339
}
340
}
341
}
342
343
/**
344
* Records the current time before performing an operation
345
*/
346
void start_timer(const char *name) {
347
struct GdTimer *timer;
348
349
if (!sTimingActive) {
350
return;
351
}
352
353
// Create timer if it does not exist.
354
timer = get_timer(name);
355
if (timer == NULL) {
356
timer = new_timer(name);
357
if (timer == NULL) {
358
fatal_printf("start_timer(): Unable to make timer '%s'", name);
359
}
360
}
361
362
timer->prevScaledTotal = timer->scaledTotal;
363
timer->start = gd_get_ostime();
364
timer->total = 0;
365
timer->resetCount = 1;
366
}
367
368
/**
369
* Records the current time before performing an operation
370
*/
371
void restart_timer(const char *name) {
372
struct GdTimer *timer;
373
374
if (!sTimingActive) {
375
return;
376
}
377
378
// Create timer if it does not exist.
379
timer = get_timer(name);
380
if (timer == NULL) {
381
timer = new_timer(name);
382
if (timer == NULL) {
383
fatal_printf("restart_timer(): Unable to make timer '%s'", name);
384
}
385
}
386
387
timer->start = gd_get_ostime();
388
timer->resetCount++;
389
}
390
391
/**
392
* Records the current time after performing an operation, adds the elapsed time
393
* to the total, then restarts the timer
394
*/
395
void split_timer(const char *name) {
396
struct GdTimer *timer;
397
398
if (!sTimingActive) {
399
return;
400
}
401
402
timer = get_timer_checked(name);
403
split_timer_ptr(timer);
404
}
405
406
/**
407
* Records the current time after performing an operation
408
*/
409
void stop_timer(const char *name) {
410
struct GdTimer *timer;
411
412
if (!sTimingActive) {
413
return;
414
}
415
416
timer = get_timer_checked(name);
417
timer->end = gd_get_ostime();
418
timer->total += timer->end - timer->start;
419
if (timer->total < 0) {
420
timer->total = 0;
421
}
422
423
timer->scaledTotal = ((f32) timer->total) / get_time_scale();
424
}
425
426
/**
427
* Returns the scaled total for the specified timer
428
*/
429
f32 get_scaled_timer_total(const char *name) {
430
struct GdTimer *timer = get_timer_checked(name);
431
432
return timer->scaledTotal;
433
}
434
435
/**
436
* Unused - returns the raw total for the specified timer
437
*/
438
f32 get_timer_total(const char *name) {
439
struct GdTimer *timer = get_timer_checked(name);
440
441
return (f32) timer->total;
442
}
443
444
445
/*
446
* Miscellaneous debug functions
447
*/
448
449
450
/**
451
* Prints the given string, prints the stack trace, and exits the program
452
*/
453
void fatal_print(const char *str) {
454
fatal_printf(str);
455
}
456
457
/**
458
* Prints the stack trace registered by callng imin()/imout()
459
*/
460
void print_stack_trace(void) {
461
s32 i;
462
463
for (i = 0; i < sNumRoutinesInStack; i++) {
464
gd_printf("\tIn: '%s'\n", sRoutineNames[i]);
465
}
466
}
467
468
/**
469
* Prints the formatted string, prints the stack trace, and exits the program
470
*/
471
void fatal_printf(const char *fmt, ...) {
472
char cur;
473
UNUSED u8 pad[4];
474
va_list vl;
475
476
va_start(vl, fmt);
477
while ((cur = *fmt++)) {
478
switch (cur) {
479
case '%':
480
switch (cur = *fmt++) {
481
case 'd':
482
gd_printf("%d", va_arg(vl, s32));
483
break;
484
case 'f':
485
gd_printf("%f", va_arg(vl, double));
486
break;
487
case 's':
488
gd_printf("%s", va_arg(vl, char *));
489
break;
490
case 'c':
491
#ifdef AVOID_UB
492
gd_printf("%c", (char)va_arg(vl, int));
493
#else
494
gd_printf("%c", va_arg(vl, char));
495
#endif
496
break;
497
case 'x':
498
gd_printf("%x", va_arg(vl, s32));
499
break;
500
default:
501
gd_printf("%c", cur);
502
}
503
break;
504
case '\\':
505
gd_printf("\\");
506
break;
507
case '\n':
508
gd_printf("\n");
509
break;
510
default:
511
gd_printf("%c", cur);
512
}
513
}
514
va_end(vl);
515
516
gd_printf("\n");
517
print_stack_trace();
518
gd_printf("\n");
519
gd_exit(-1);
520
}
521
522
/**
523
* "I'm in"
524
* Adds the function name to the stack trace
525
*/
526
void imin(const char *routine) {
527
sRoutineNames[sNumRoutinesInStack++] = routine;
528
sRoutineNames[sNumRoutinesInStack] = NULL; //! array bounds is checked after writing this.
529
530
if (sNumRoutinesInStack >= ARRAY_COUNT(sRoutineNames)) {
531
fatal_printf("You're in too many routines");
532
}
533
}
534
535
/**
536
* "I'm out"
537
* Removes the function name from the stack trace
538
*/
539
void imout(void) {
540
s32 i;
541
542
if (--sNumRoutinesInStack < 0) {
543
for (i = 0; i < ARRAY_COUNT(sRoutineNames); i++) {
544
if (sRoutineNames[i] != NULL) {
545
gd_printf(" - %s\n", sRoutineNames[i]);
546
} else {
547
break;
548
}
549
}
550
551
fatal_printf("imout() - imout() called too many times");
552
}
553
}
554
555
/**
556
* Returns a random floating point number between 0 and 1 (inclusive)
557
* TODO: figure out type of rng generator?
558
*/
559
f32 gd_rand_float(void) {
560
u32 temp;
561
u32 i;
562
f32 val;
563
564
for (i = 0; i < 4; i++) {
565
if (sPrimarySeed & 0x80000000) {
566
sPrimarySeed = sPrimarySeed << 1 | 1;
567
} else {
568
sPrimarySeed <<= 1;
569
}
570
}
571
sPrimarySeed += 4;
572
573
/* Seed Switch */
574
if ((sPrimarySeed ^= gd_get_ostime()) & 1) {
575
temp = sPrimarySeed;
576
sPrimarySeed = sSecondarySeed;
577
sSecondarySeed = temp;
578
}
579
580
val = (sPrimarySeed & 0xFFFF) / 65535.0; // 65535.0f
581
582
return val;
583
}
584
585
/**
586
* Reimplementation of the standard "atoi" function
587
*/
588
s32 gd_atoi(const char *str) {
589
char cur;
590
const char *origstr = str;
591
s32 curval;
592
s32 out = 0;
593
s32 isNegative = FALSE;
594
595
while (TRUE) {
596
cur = *str++;
597
598
// Each character must be either a digit or a minus sign
599
if ((cur < '0' || cur > '9') && (cur != '-'))
600
fatal_printf("gd_atoi() bad number '%s'", origstr);
601
602
if (cur == '-') {
603
isNegative = TRUE;
604
} else {
605
curval = cur - '0';
606
out += curval & 0xFF;
607
608
if (*str == '\0' || *str == '.' || *str < '0' || *str > '9') {
609
break;
610
}
611
612
out *= 10;
613
}
614
}
615
616
if (isNegative) {
617
out = -out;
618
}
619
620
return out;
621
}
622
623
/**
624
* Like the standard "atof" function, but only supports integer values
625
*/
626
f64 gd_lazy_atof(const char *str, UNUSED u32 *unk) {
627
return gd_atoi(str);
628
}
629
630
static char sHexNumerals[] = {"0123456789ABCDEF"};
631
632
/* 23C018 -> 23C078; orig name: func_8018D848 */
633
char *format_number_hex(char *str, s32 val) {
634
s32 shift;
635
636
for (shift = 28; shift > -4; shift -= 4) {
637
*str++ = sHexNumerals[(val >> shift) & 0xF];
638
}
639
640
*str = '\0';
641
642
return str;
643
}
644
645
static s32 sPadNumPrint = 0; // @ 801A82C0
646
647
/* 23C078 -> 23C174; orig name: func_8018D8A8 */
648
/* padnum = a decimal number with the max desired output width */
649
char *format_number_decimal(char *str, s32 val, s32 padnum) {
650
s32 i;
651
652
if (val == 0) {
653
*str++ = '0';
654
*str = '\0';
655
return str;
656
}
657
658
if (val < 0) {
659
val = -val;
660
*str++ = '-';
661
}
662
663
while (padnum > 0) {
664
if (padnum <= val) {
665
sPadNumPrint = TRUE;
666
667
for (i = 0; i < 9; i++) {
668
val -= padnum;
669
if (val < 0) {
670
val += padnum;
671
break;
672
}
673
}
674
675
*str++ = i + '0';
676
} else {
677
if (sPadNumPrint) {
678
*str++ = '0';
679
}
680
}
681
682
padnum /= 10;
683
}
684
685
*str = '\0';
686
687
return str;
688
}
689
690
/* 23C174 -> 23C1C8; orig name: func_8018D9A4 */
691
static s32 int_sci_notation(s32 base, s32 significand) {
692
s32 i;
693
694
for (i = 1; i < significand; i++) {
695
base *= 10;
696
}
697
698
return base;
699
}
700
701
/* 23C1C8 -> 23C468; orig name: func_8018D9F8 */
702
char *sprint_val_withspecifiers(char *str, union PrintVal val, char *specifiers) {
703
s32 fracPart; // sp3C
704
s32 intPart; // sp38
705
s32 intPrec; // sp34
706
s32 fracPrec; // sp30
707
UNUSED u8 pad[4];
708
char cur; // sp2B
709
710
fracPrec = 6;
711
intPrec = 6;
712
713
while ((cur = *specifiers++)) {
714
if (cur == 'd') {
715
sPadNumPrint = FALSE;
716
str = format_number_decimal(str, val.i, 1000000000);
717
} else if (cur == 'x') {
718
sPadNumPrint = TRUE; /* doesn't affect hex printing, though... */
719
str = format_number_hex(str, val.i);
720
} else if (cur == 'f') {
721
intPart = (s32) val.f;
722
fracPart = (s32)((val.f - (f32) intPart) * (f32) int_sci_notation(10, fracPrec));
723
sPadNumPrint = FALSE;
724
str = format_number_decimal(str, intPart, int_sci_notation(10, intPrec));
725
*str++ = '.';
726
sPadNumPrint = TRUE;
727
str = format_number_decimal(str, fracPart, int_sci_notation(10, fracPrec - 1));
728
} else if (cur >= '0' && cur <= '9') {
729
cur = cur - '0';
730
intPrec = cur;
731
if (*specifiers++) {
732
fracPrec = (*specifiers++) - '0';
733
}
734
} else {
735
gd_strcpy(str, "<BAD TYPE>");
736
str += 10;
737
}
738
}
739
740
return str;
741
}
742
743
/* 23C468 -> 23C4AC; orig name: func_8018DC98 */
744
void gd_strcpy(char *dst, const char *src) {
745
while ((*dst++ = *src++)) {
746
;
747
}
748
}
749
750
/* 23C4AC -> 23C52C; not called; orig name: Unknown8018DCDC */
751
void ascii_to_uppercase(char *str) {
752
char c;
753
754
while ((c = *str)) {
755
if (c >= 'a' && c <= 'z') {
756
*str = c & 0xDF;
757
}
758
str++;
759
}
760
}
761
762
/* 23C52C -> 23C5A8; orig name: func_8018DD5C */
763
char *gd_strdup(const char *src) {
764
char *dst; // sp24
765
766
dst = gd_malloc_perm((gd_strlen(src) + 1) * sizeof(char));
767
768
if (dst == NULL) {
769
fatal_printf("gd_strdup(): out of memory");
770
}
771
gd_strcpy(dst, src);
772
773
return dst;
774
}
775
776
/* 23C5A8 -> 23C5FC; orig name: func_8018DDD8 */
777
u32 gd_strlen(const char *str) {
778
u32 len = 0;
779
780
while (*str++) {
781
len++;
782
}
783
784
return len;
785
}
786
787
/* 23C5FC -> 23C680; orig name: func_8018DE2C */
788
char *gd_strcat(char *dst, const char *src) {
789
while (*dst++) {
790
;
791
}
792
793
if (*src) {
794
dst--;
795
while ((*dst++ = *src++)) {
796
;
797
}
798
}
799
800
return --dst;
801
}
802
803
/* 23C67C -> 23C728; orig name: func_8018DEB0 */
804
/* Returns a bool, not the position of the mismatch */
805
s32 gd_str_not_equal(const char *str1, const char *str2) {
806
while (*str1 && *str2) {
807
if (*str1++ != *str2++) {
808
return TRUE;
809
}
810
}
811
812
return *str1 != '\0' || *str2 != '\0';
813
}
814
815
/* 23C728 -> 23C7B8; orig name; func_8018DF58 */
816
s32 gd_str_contains(const char *str1, const char *str2) {
817
const char *startsub = str2;
818
819
while (*str1 && *str2) {
820
if (*str1++ != *str2++) {
821
str2 = startsub;
822
}
823
}
824
825
return !*str2;
826
}
827
828
/* 23C7B8 -> 23C7DC; orig name: func_8018DFE8 */
829
s32 gd_feof(struct GdFile *f) {
830
return f->flags & 0x4;
831
}
832
833
/* 23C7DC -> 23C7FC; orig name: func_8018E00C */
834
void gd_set_feof(struct GdFile *f) {
835
f->flags |= 0x4;
836
}
837
838
/* 23C7FC -> 23CA0C */
839
struct GdFile *gd_fopen(const char *filename, const char *mode) {
840
struct GdFile *f; // sp74
841
char *loadedname; // sp70
842
u32 i; // sp6C
843
UNUSED u32 pad68;
844
struct UnkBufThing buf; // sp24
845
u8 *bufbytes; // sp20
846
u8 *fileposptr; // sp1C
847
s32 filecsr; // sp18
848
849
filecsr = 0;
850
851
while (TRUE) {
852
bufbytes = (u8 *) &buf;
853
for (i = 0; i < sizeof(struct UnkBufThing); i++) {
854
*bufbytes++ = gGdStreamBuffer[filecsr++];
855
}
856
stub_renderer_13(&buf);
857
fileposptr = &gGdStreamBuffer[filecsr];
858
filecsr += buf.size;
859
860
loadedname = buf.name;
861
862
if (buf.size == 0) {
863
break;
864
}
865
if (!gd_str_not_equal(filename, loadedname)) {
866
break;
867
}
868
}
869
870
if (buf.size == 0) {
871
fatal_printf("gd_fopen() File not found '%s'", filename);
872
return NULL;
873
}
874
875
f = gd_malloc_perm(sizeof(struct GdFile));
876
if (f == NULL) {
877
fatal_printf("gd_fopen() Out of memory loading '%s'", filename);
878
return NULL;
879
}
880
881
f->stream = (s8 *) fileposptr;
882
f->size = buf.size;
883
f->pos = f->flags = 0;
884
if (gd_str_contains(mode, "w")) {
885
f->flags |= 0x1;
886
}
887
if (gd_str_contains(mode, "b")) {
888
f->flags |= 0x2;
889
}
890
891
return f;
892
}
893
894
/* 23CA0C -> 23CB38; orig name: func_8018E23C */
895
s32 gd_fread(s8 *buf, s32 bytes, UNUSED s32 count, struct GdFile *f) {
896
s32 bytesToRead = bytes;
897
s32 bytesread;
898
899
if (f->pos + bytesToRead > f->size) {
900
bytesToRead = f->size - f->pos;
901
}
902
903
if (bytesToRead == 0) {
904
gd_set_feof(f);
905
return -1;
906
}
907
908
bytesread = bytesToRead;
909
while (bytesread--) {
910
*buf++ = f->stream[f->pos++];
911
}
912
913
return bytesToRead;
914
}
915
916
/* 23CB38 -> 23CB54; orig name: func_8018E368 */
917
void gd_fclose(UNUSED struct GdFile *f) {
918
return;
919
}
920
921
/* 23CB54 -> 23CB70; orig name: func_8018E384 */
922
u32 gd_get_file_size(struct GdFile *f) {
923
return f->size;
924
}
925
926
/* 23CB70 -> 23CBA8; orig name: func_8018E3A0 */
927
s32 is_newline(char c) {
928
return c == '\r' || c == '\n';
929
}
930
931
/* 23CBA8 -> 23CCF0; orig name: func_8018E3D8 */
932
s32 gd_fread_line(char *buf, u32 size, struct GdFile *f) {
933
signed char c;
934
u32 pos = 0;
935
UNUSED u32 pad1c;
936
937
do {
938
if (gd_fread(&c, 1, 1, f) == -1) {
939
break;
940
}
941
} while (is_newline(c));
942
943
while (!is_newline(c)) {
944
if (c == -1) {
945
break;
946
}
947
if (pos > size) {
948
break;
949
}
950
buf[pos++] = c;
951
if (gd_fread(&c, 1, 1, f) == -1) {
952
break;
953
}
954
}
955
buf[pos++] = '\0';
956
957
return pos;
958
}
959
960