Skip to content

Commit 74b8d76

Browse files
committed
Merge from master.
2 parents b622c00 + 7aebef7 commit 74b8d76

5 files changed

Lines changed: 129 additions & 61 deletions

File tree

.github/workflows/ccpp.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ jobs:
1414
variant:
1515
- debian12
1616
- debian13
17-
- fedora40
18-
- fedora41
1917
- fedora42
2018
- fedora43
2119
- fedora43.nooptionaldeps

Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
ifeq ($(BUILDTYPE),)
2+
# On MSYS2 uname -s produces something like: MINGW64_NT-10.0-19045
3+
# Strip the suffix off
4+
OS := $(patsubst MINGW%,MINGW,$(shell uname -s))
25
buildtype_Darwin = osx
36
buildtype_Haiku = haiku
4-
BUILDTYPE := $(buildtype_$(shell uname -s ))
7+
buildtype_MINGW = windows
8+
BUILDTYPE := $(buildtype_$(OS))
59
ifeq ($(BUILDTYPE),)
610
BUILDTYPE := unix
711
endif

lib/vfs/smaky6fs.cc

Lines changed: 117 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,19 @@
55

66
/* A directory entry looks like:
77
*
8-
* 00-09: ten byte filename FFFFFFFF.EE
9-
* 0a-0b: word: start sector
10-
* 0c-17: unknown
8+
* 00-07: name, space-padded ASCII (high bit may be set as attribute flag → mask with 0x7f)
9+
* 08-09: 2-char type code (SY, SM, ST, SR, LS, FH, BS, IM, KS …)
10+
* 0a-0b: uint16 LE start_sector – first data sector (0-based absolute)
11+
* 0c-0d: uint16 LE end_sector – first sector after the file (exclusive)
12+
* 0e-0f: uint16 LE flags – purpose unknown
13+
* 10-11: uint16 LE last_bytes – bytes used in last sector;
14+
* 0 means last sector is completely full
15+
* exact_size = (size_sectors-1)*256 + last_bytes if last_bytes > 0
16+
* size_sectors * 256 if last_bytes == 0
17+
* 12-13: load address – high byte then low byte (reliable for type SM)
18+
* 14-15: entry point – same encoding
19+
* 16: month BCD (0x07 = July); 0x00 or 0xFF = no date
20+
* 17: year BCD (0x82 = 1982); 0x00 or 0xFF = no date
1121
*/
1222

1323
class Smaky6Filesystem : public Filesystem
@@ -57,37 +67,62 @@ class Smaky6Filesystem : public Filesystem
5767
filename = ss.str();
5868
}
5969

60-
std::string metadataBytes;
61-
{
62-
std::stringstream ss;
63-
64-
for (int i = 10; i < 0x18; i++)
65-
ss << fmt::format("{:02x} ", (uint8_t)dbuf[i]);
66-
67-
metadataBytes = ss.str();
68-
}
69-
7070
ByteReader br(dbuf);
71-
72-
br.skip(10); /* filename */
71+
br.skip(10); /* skip filename/type already parsed above */
7372
startSector = br.read_le16();
74-
endSector = br.read_le16();
75-
br.skip(2); /* unknown */
73+
endSector = br.read_le16();
74+
uint16_t flags = br.read_le16(); /* purpose unknown */
7675
lastSectorLength = br.read_le16();
77-
78-
file_type = TYPE_FILE;
79-
length = (endSector - startSector - 1) * 256 + lastSectorLength;
76+
uint8_t loadHi = br.read_8();
77+
uint8_t loadLo = br.read_8();
78+
uint8_t entryHi = br.read_8();
79+
uint8_t entryLo = br.read_8();
80+
uint8_t monthBcd = br.read_8();
81+
uint8_t yearBcd = br.read_8();
82+
83+
/* Decode BCD date; 0x00 and 0xFF both mean "no date" */
84+
auto bcdToInt = [](uint8_t b) -> int {
85+
return (b >> 4) * 10 + (b & 0x0f);
86+
};
87+
int month = (monthBcd && monthBcd != 0xff) ? bcdToInt(monthBcd) : 0;
88+
int year = (yearBcd && yearBcd != 0xff) ? bcdToInt(yearBcd) : 0;
89+
90+
uint16_t loadAddr = ((uint16_t)loadHi << 8) | loadLo;
91+
uint16_t entryAddr = ((uint16_t)entryHi << 8) | entryLo;
92+
93+
/* DR entries are sub-directory containers, not plain files. */
94+
file_type = (filename.size() > 3 &&
95+
filename.substr(filename.size() - 3) == ".DR")
96+
? TYPE_DIRECTORY : TYPE_FILE;
97+
/* When lastSectorLength == 0 the last sector is completely full;
98+
* FluxEngine's original formula subtracted 256 bytes in that case. */
99+
length = lastSectorLength
100+
? (endSector - startSector - 1) * 256 + lastSectorLength
101+
: (endSector - startSector) * 256;
80102

81103
path = {filename};
82104
attributes[Filesystem::FILENAME] = filename;
83-
attributes[Filesystem::LENGTH] = std::to_string(length);
105+
attributes[Filesystem::LENGTH] = std::to_string(length);
84106
attributes[Filesystem::FILE_TYPE] = "file";
85-
attributes[Filesystem::MODE] = "";
107+
attributes[Filesystem::MODE] = "";
86108
attributes["smaky6.start_sector"] = std::to_string(startSector);
87-
attributes["smaky6.end_sector"] = std::to_string(endSector);
88-
attributes["smaky6.sectors"] =
89-
std::to_string(endSector - startSector);
90-
attributes["smaky6.metadata_bytes"] = metadataBytes;
109+
attributes["smaky6.end_sector"] = std::to_string(endSector);
110+
attributes["smaky6.sectors"] = std::to_string(endSector - startSector);
111+
attributes["smaky6.flags"] = fmt::format("0x{:04x}", flags);
112+
if (loadAddr)
113+
attributes["smaky6.load_addr"] = fmt::format("0x{:04x}", loadAddr);
114+
if (entryAddr)
115+
attributes["smaky6.entry_addr"] = fmt::format("0x{:04x}", entryAddr);
116+
if (month && year)
117+
{
118+
static const char* months[] = {"","Jan","Feb","Mar","Apr","May","Jun",
119+
"Jul","Aug","Sep","Oct","Nov","Dec"};
120+
int yearFull = (year >= 78) ? 1900 + year : 2000 + year;
121+
attributes["smaky6.date"] =
122+
fmt::format("{} {}",
123+
(month >= 1 && month <= 12) ? months[month] : "?",
124+
yearFull);
125+
}
91126
}
92127

93128
public:
@@ -100,33 +135,42 @@ class Smaky6Filesystem : public Filesystem
100135
class Directory
101136
{
102137
public:
103-
Directory(Smaky6Filesystem* fs)
138+
/* drStartSector=0 reads the root directory; any other value reads
139+
* the sub-directory stored in the first 3 sectors of a DR entry. */
140+
Directory(Smaky6Filesystem* fs, unsigned drStartSector = 0)
141+
{
142+
auto bytes = fs->getLogicalSector(drStartSector, 3);
143+
parseFrom(bytes, drStartSector);
144+
}
145+
146+
std::shared_ptr<SmakyDirent> findFile(const std::string& filename)
104147
{
105-
/* Read the directory. */
148+
for (auto& de : dirents)
149+
if (de->filename == filename)
150+
return de;
106151

107-
auto bytes = fs->getLogicalSector(0, 3);
108-
ByteReader br(bytes);
152+
throw FileNotFoundException();
153+
}
109154

155+
private:
156+
void parseFrom(const Bytes& bytes, unsigned sectorBase)
157+
{
110158
for (int i = 0; i < 32; i++)
111159
{
112160
auto dbuf = bytes.slice(i * 0x18, 0x18);
113-
if (dbuf[0])
161+
/* 0x00 = empty slot; 0xFF = deleted entry */
162+
if (dbuf[0] && dbuf[0] != 0xff)
114163
{
115164
auto de = std::make_shared<SmakyDirent>(dbuf);
165+
/* Sub-directory entries use relative sector numbers;
166+
* add the DR container's base sector to get absolute ones. */
167+
de->startSector += sectorBase;
168+
de->endSector += sectorBase;
116169
dirents.push_back(de);
117170
}
118171
}
119172
}
120173

121-
std::shared_ptr<SmakyDirent> findFile(const std::string& filename)
122-
{
123-
for (auto& de : dirents)
124-
if (de->filename == filename)
125-
return de;
126-
127-
throw FileNotFoundException();
128-
}
129-
130174
public:
131175
std::vector<std::shared_ptr<SmakyDirent>> dirents;
132176
};
@@ -167,40 +211,55 @@ class Smaky6Filesystem : public Filesystem
167211

168212
std::vector<std::shared_ptr<Dirent>> list(const Path& path) override
169213
{
170-
if (!path.empty())
171-
throw FileNotFoundException();
172-
173-
Directory dir(this);
174214
std::vector<std::shared_ptr<Dirent>> result;
175-
for (auto& de : dir.dirents)
215+
for (auto& de : directoryAt(path).dirents)
176216
result.push_back(de);
177217
return result;
178218
}
179219

180220
std::shared_ptr<Dirent> getDirent(const Path& path) override
181221
{
182-
Directory dir(this);
183-
if (path.size() != 1)
184-
throw BadPathException();
185-
186-
return dir.findFile(path[0]);
222+
return resolveDirent(path);
187223
}
188224

189225
Bytes getFile(const Path& path) override
190226
{
191-
if (path.size() != 1)
227+
auto de = resolveDirent(path);
228+
if (de->file_type == TYPE_DIRECTORY)
192229
throw BadPathException(path);
230+
Bytes data = getLogicalSector(
231+
de->startSector, de->endSector - de->startSector);
232+
return data.slice(0, de->length);
233+
}
193234

194-
Directory dir(this);
195-
auto de = dir.findFile(path[0]);
235+
private:
236+
/* Returns the Directory whose entries are named by path.
237+
* path=[] → root; path=["DIR.DR"] → DR sub-directory.
238+
* The Smaky 6 FS is at most two levels deep; deeper paths are invalid. */
239+
Directory directoryAt(const Path& path)
240+
{
241+
if (path.empty())
242+
return Directory(this);
243+
if (path.size() == 1)
244+
{
245+
auto parent = Directory(this).findFile(path[0]);
246+
if (parent->file_type != TYPE_DIRECTORY)
247+
throw BadPathException(path);
248+
return Directory(this, parent->startSector);
249+
}
250+
throw BadPathException(path);
251+
}
196252

197-
Bytes data =
198-
getLogicalSector(de->startSector, de->endSector - de->startSector);
199-
data = data.slice(0, de->length);
200-
return data;
253+
/* Resolves a full path to its SmakyDirent.
254+
* path=["FILE"] → file in root; path=["DIR.DR","FILE"] → file in sub-dir. */
255+
std::shared_ptr<SmakyDirent> resolveDirent(const Path& path)
256+
{
257+
if (path.empty())
258+
throw BadPathException(path);
259+
Path parentPath(path.begin(), path.end() - 1);
260+
return directoryAt(parentPath).findFile(path.back());
201261
}
202262

203-
private:
204263
const Smaky6FsProto& _config;
205264
};
206265

lib/vfs/vfs.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ Path::Path(const std::vector<std::string> other):
2323
{
2424
}
2525

26+
Path::Path(const std::vector<std::string>::const_iterator& begin, const std::vector<std::string>::const_iterator& end):
27+
std::vector<std::string>(begin, end)
28+
{
29+
}
30+
2631
Path::Path(const std::string& path)
2732
{
2833
if (path == "")

lib/vfs/vfs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ class Path : public std::vector<std::string>
1616
{
1717
public:
1818
Path() {}
19+
Path(const std::vector<std::string>::const_iterator& begin,
20+
const std::vector<std::string>::const_iterator& end);
1921
Path(const std::vector<std::string> other);
2022
Path(const std::string& text);
2123

0 commit comments

Comments
 (0)