-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathspherecas.c
132 lines (126 loc) · 3.79 KB
/
spherecas.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//
// spherecas.c
//
// Copyright (c) Ben Zotto 2022
// See LICENSE for licensing information.
//
#include "spherecas.h"
#define HEADER_SYNC 0x16
#define HEADER_ESC 0x1B
#define HEADER_ETB 0x17
enum spherecas_read_state {
READ_SYNC = 0,
READ_HEADER_START,
READ_DATA_LENGTH_HIGH,
READ_DATA_LENGTH_LOW,
READ_BLOCK_NAME_1,
READ_BLOCK_NAME_2,
READ_DATA,
READ_ETB,
READ_CHECKSUM
};
void spherecas_begin_read(struct spherecas_state * state)
{
state->read_state = READ_SYNC;
state->data_count_expected = 0;
state->data_count_read = 0;
state->checksum = 0;
state->block_type = SPHERECAS_BLOCKTYPE_TEXT;
}
void spherecas_read_byte(struct spherecas_state * state, uint8_t byte)
{
switch (state->read_state) {
case READ_SYNC:
{
if (byte == HEADER_SYNC) {
state->read_state = READ_HEADER_START;
}
break;
}
case READ_HEADER_START:
{
if (byte == HEADER_ESC) {
state->read_state = READ_DATA_LENGTH_HIGH;
} else if (byte == HEADER_SYNC) {
// Do nothing, remain in this state.
;
} else {
// Resync
state->read_state = READ_SYNC;
}
break;
}
case READ_DATA_LENGTH_HIGH:
{
state->data_count_expected = (byte << 8);
state->read_state++;
break;
}
case READ_DATA_LENGTH_LOW:
{
state->data_count_expected |= byte;
state->data_count_expected++; // Actual block data count is length+1
state->read_state++;
break;
}
case READ_BLOCK_NAME_1:
{
state->block_name[0] = byte;
state->read_state++;
break;
}
case READ_BLOCK_NAME_2:
{
state->block_name[1] = byte;
state->read_state++;
break;
}
case READ_DATA:
{
state->data[state->data_count_read++] = byte;
state->checksum += byte;
if (state->data_count_read == state->data_count_expected) {
state->read_state++;
}
// If any values in the block have their high bit set, the block
// is likely to contain object code. If all bytes are 7-bit ASCII
// then the block is likely to be text or source (the default).
// This is the heuristic used by Programma's Tape Directory program.
if (byte & 0x80) {
state->block_type = SPHERECAS_BLOCKTYPE_OBJECT;
}
break;
}
case READ_ETB:
{
if (byte == HEADER_ETB) {
state->read_state = READ_CHECKSUM;
} else {
// Report out an error with this block.
spherecas_block_read(state, state->block_name, state->data, state->data_count_read, state->block_type, SPHERECAS_ERROR_TRAILER);
// Go back to sync here.
spherecas_begin_read(state);
}
break;
}
case READ_CHECKSUM:
{
enum spherecas_error error = SPHERECAS_ERROR_NONE;
if (byte != state->checksum) {
error = SPHERECAS_ERROR_CHECKSUM;
}
spherecas_block_read(state, state->block_name, state->data, state->data_count_read, state->block_type, error);
spherecas_begin_read(state);
break;
}
}
}
void spherecas_read_bytes(struct spherecas_state * restrict state,
const uint8_t * restrict data,
int count)
{
while (count > 0) {
spherecas_read_byte(state, *data++);
--count;
}
}