Non-functioning archival checkin.

This commit is contained in:
David Given
2025-08-12 20:31:54 +01:00
parent 022df995aa
commit 70bdcd0978
15 changed files with 301 additions and 108 deletions

View File

@@ -387,6 +387,12 @@ sources.)
Shows all the components inside a flux file.
- `fluxfile rm <fluxfile>:<path>...`
Removes flux from a flux file. You may specify the path to an individual read
(e.g. `track.h0_t5.flux9`) or the track itself (`track.h0_t5`); the latter
will remove all reads from the track.
### High density disks
High density disks use a different magnetic medium to low and double density

View File

@@ -1,6 +1,7 @@
#include "lib/core/globals.h"
#include "lib/config/proto.h"
#include "lib/config/common.pb.h"
#include "google/protobuf/reflection.h"
#include <regex>
static ConfigProto config = []()
@@ -16,7 +17,7 @@ ConfigProto& globalConfigProto()
return config;
}
static double toFloat(const std::string& value)
static float toFloat(const std::string& value)
{
try
{
@@ -87,6 +88,21 @@ void setRange(RangeProto* range, const std::string& data)
range->set_step(std::stoi(dmatch[4]));
}
static int splitIndexedField(std::string& item)
{
static const std::regex INDEX_REGEX("(\\w+)\\[([0-9]+)\\]");
int index = -1;
std::smatch dmatch;
if (std::regex_match(item, dmatch, INDEX_REGEX))
{
item = dmatch[1];
index = std::stoi(dmatch[2]);
}
return index;
}
static ProtoField resolveProtoPath(
google::protobuf::Message* message, const std::string& path, bool create)
{
@@ -109,13 +125,7 @@ static ProtoField resolveProtoPath(
{
static const std::regex INDEX_REGEX("(\\w+)\\[([0-9]+)\\]");
int index = -1;
std::smatch dmatch;
if (std::regex_match(item, dmatch, INDEX_REGEX))
{
item = dmatch[1];
index = std::stoi(dmatch[2]);
}
int index = splitIndexedField(item);
const auto* field = descriptor->FindFieldByName(item);
if (!field)
@@ -163,11 +173,12 @@ static ProtoField resolveProtoPath(
descriptor = message->GetDescriptor();
}
int index = splitIndexedField(trailing);
const auto* field = descriptor->FindFieldByName(trailing);
if (!field)
fail();
return std::make_pair(message, field);
return std::make_tuple(message, field, -1);
}
ProtoField makeProtoPath(
@@ -182,98 +193,208 @@ ProtoField findProtoPath(
return resolveProtoPath(message, path, /* create= */ false);
}
static bool parseBoolean(const std::string& value)
{
static const std::map<std::string, bool> boolvalues = {
{"false", false},
{"f", false},
{"no", false},
{"n", false},
{"0", false},
{"true", true },
{"t", true },
{"yes", true },
{"y", true },
{"1", true },
};
const auto& it = boolvalues.find(value);
if (it == boolvalues.end())
error("invalid boolean value");
return it->second;
}
static int32_t parseEnum(
const google::protobuf::FieldDescriptor* field, const std::string& value)
{
const auto* enumfield = field->enum_type();
const auto* enumvalue = enumfield->FindValueByName(value);
if (!enumvalue)
error("unrecognised enum value '{}'", value);
return enumvalue->index();
}
template <typename T>
static void updateRepeatedField(
google::protobuf::MutableRepeatedFieldRef<T> mrfr, int index, T value)
{
mrfr.Set(index, value);
}
void setProtoFieldFromString(ProtoField& protoField, const std::string& value)
{
google::protobuf::Message* message = protoField.first;
const google::protobuf::FieldDescriptor* field = protoField.second;
auto& [message, field, index] = protoField;
const auto* reflection = message->GetReflection();
switch (field->type())
if (field->label() == google::protobuf::FieldDescriptor::LABEL_REPEATED)
{
case google::protobuf::FieldDescriptor::TYPE_FLOAT:
reflection->SetFloat(message, field, toFloat(value));
break;
if (index == -1)
error("field '{}' is repeated but no index is provided");
case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
reflection->SetDouble(message, field, toDouble(value));
break;
case google::protobuf::FieldDescriptor::TYPE_INT32:
reflection->SetInt32(message, field, toInt64(value));
break;
case google::protobuf::FieldDescriptor::TYPE_INT64:
reflection->SetInt64(message, field, toInt64(value));
break;
case google::protobuf::FieldDescriptor::TYPE_UINT32:
reflection->SetUInt32(message, field, toUint64(value));
break;
case google::protobuf::FieldDescriptor::TYPE_UINT64:
reflection->SetUInt64(message, field, toUint64(value));
break;
case google::protobuf::FieldDescriptor::TYPE_STRING:
reflection->SetString(message, field, value);
break;
case google::protobuf::FieldDescriptor::TYPE_BOOL:
switch (field->type())
{
static const std::map<std::string, bool> boolvalues = {
{"false", false},
{"f", false},
{"no", false},
{"n", false},
{"0", false},
{"true", true },
{"t", true },
{"yes", true },
{"y", true },
{"1", true },
};
case google::protobuf::FieldDescriptor::TYPE_FLOAT:
updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<float>(
message, field),
index,
toFloat(value));
break;
const auto& it = boolvalues.find(value);
if (it == boolvalues.end())
error("invalid boolean value");
reflection->SetBool(message, field, it->second);
break;
}
case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<double>(
message, field),
index,
toDouble(value));
break;
case google::protobuf::FieldDescriptor::TYPE_ENUM:
{
const auto* enumfield = field->enum_type();
const auto* enumvalue = enumfield->FindValueByName(value);
if (!enumvalue)
error("unrecognised enum value '{}'", value);
case google::protobuf::FieldDescriptor::TYPE_INT32:
updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<int32_t>(
message, field),
index,
(int32_t)toInt64(value));
break;
reflection->SetEnum(message, field, enumvalue);
break;
}
case google::protobuf::FieldDescriptor::TYPE_INT64:
updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<int64_t>(
message, field),
index,
toInt64(value));
break;
case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
if (field->message_type() == RangeProto::descriptor())
{
setRange(
(RangeProto*)reflection->MutableMessage(message, field),
case google::protobuf::FieldDescriptor::TYPE_UINT32:
updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<uint32_t>(
message, field),
index,
(uint32_t)toUInt64(value));
break;
case google::protobuf::FieldDescriptor::TYPE_UINT64:
updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<uint64_t>(
message, field),
index,
toUInt64(value));
break;
case google::protobuf::FieldDescriptor::TYPE_STRING:
updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<std::string>(
message, field),
index,
value);
break;
}
if (field->containing_oneof() && value.empty())
{
reflection->MutableMessage(message, field);
case google::protobuf::FieldDescriptor::TYPE_BOOL:
updateRepeatedField(
reflection->GetMutableRepeatedFieldRef<bool>(
message, field),
index,
parseBoolean(value));
break;
}
/* fall through */
default:
error("can't set this config value type");
case google::protobuf::FieldDescriptor::TYPE_ENUM:
reflection->SetRepeatedEnum(
message, field, index, parseEnum(field, value));
break;
case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
if (field->message_type() == RangeProto::descriptor())
{
setRange((RangeProto*)reflection->MutableRepeatedMessage(
message, field, index),
value);
break;
}
if (field->containing_oneof() && value.empty())
{
reflection->MutableRepeatedMessage(message, field, index);
break;
}
/* fall through */
default:
error("can't set this config value type");
}
}
else
{
if (index != -1)
error("field '{}' is not repeated but an index is provided");
switch (field->type())
{
case google::protobuf::FieldDescriptor::TYPE_FLOAT:
reflection->SetFloat(message, field, toFloat(value));
break;
case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
reflection->SetDouble(message, field, toDouble(value));
break;
case google::protobuf::FieldDescriptor::TYPE_INT32:
reflection->SetInt32(message, field, toInt64(value));
break;
case google::protobuf::FieldDescriptor::TYPE_INT64:
reflection->SetInt64(message, field, toInt64(value));
break;
case google::protobuf::FieldDescriptor::TYPE_UINT32:
reflection->SetUInt32(message, field, toUint64(value));
break;
case google::protobuf::FieldDescriptor::TYPE_UINT64:
reflection->SetUInt64(message, field, toUint64(value));
break;
case google::protobuf::FieldDescriptor::TYPE_STRING:
reflection->SetString(message, field, value);
break;
case google::protobuf::FieldDescriptor::TYPE_BOOL:
reflection->SetBool(message, field, parseBoolean(value));
break;
case google::protobuf::FieldDescriptor::TYPE_ENUM:
reflection->SetEnum(message, field, parseEnum(field, value));
break;
case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
if (field->message_type() == RangeProto::descriptor())
{
setRange(
(RangeProto*)reflection->MutableMessage(message, field),
value);
break;
}
if (field->containing_oneof() && value.empty())
{
reflection->MutableMessage(message, field);
break;
}
/* fall through */
default:
error("can't set this config value type");
}
}
}
std::string getProtoFieldValue(ProtoField& protoField)
{
google::protobuf::Message* message = protoField.first;
const google::protobuf::FieldDescriptor* field = protoField.second;
auto& [message, field, index] = protoField;
const auto* reflection = message->GetReflection();
switch (field->type())
@@ -363,8 +484,8 @@ std::set<unsigned> iterate(unsigned start, unsigned count)
static bool shouldRecurse(const google::protobuf::FieldDescriptor* f)
{
if (f->type() != google::protobuf::FieldDescriptor::TYPE_MESSAGE)
return false;
return f->message_type()->options().GetExtension(::recurse);
return false;
return f->message_type()->options().GetExtension(::recurse);
}
std::map<std::string, const google::protobuf::FieldDescriptor*>

View File

@@ -16,8 +16,8 @@ public:
extern void setRange(RangeProto* range, const std::string& data);
typedef std::pair<google::protobuf::Message*,
const google::protobuf::FieldDescriptor*>
typedef std::tuple<google::protobuf::Message*,
const google::protobuf::FieldDescriptor*, int>
ProtoField;
extern ProtoField makeProtoPath(

View File

@@ -1,5 +1,12 @@
syntax = "proto2";
import "google/protobuf/descriptor.proto";
extend google.protobuf.FieldOptions
{
optional bool isflux = 60000 [default = false];
}
enum FluxMagic {
MAGIC = 0x466c7578;
}
@@ -12,7 +19,7 @@ enum FluxFileVersion {
message TrackFluxProto {
optional int32 track = 1;
optional int32 head = 2;
repeated bytes flux = 3;
repeated bytes flux = 3 [(isflux) = true];
}
enum DriveType {

View File

@@ -1,7 +1,6 @@
syntax = "proto2";
import "lib/config/common.proto";
import "lib/config/layout.proto";
message AcornDfsProto
{

View File

@@ -5,9 +5,12 @@ cxxprogram(
srcs=[
"./fluxengine.cc",
"./fluxengine.h",
"./fluxfile.cc",
"./fluxfile.h",
"./fe-analysedriveresponse.cc",
"./fe-analyselayout.cc",
"./fe-fluxfilels.cc",
"./fe-fluxfilerm.cc",
"./fe-format.cc",
"./fe-getdiskinfo.cc",
"./fe-getfile.cc",
@@ -39,6 +42,7 @@ cxxprogram(
"+z_lib",
"dep/adflib",
"dep/agg",
"dep/alphanum",
"dep/fatfs",
"dep/hfsutils",
"dep/libusbp",

View File

@@ -6,6 +6,7 @@
#include "lib/data/flux.h"
#include "lib/external/fl2.h"
#include "lib/external/fl2.pb.h"
#include "dep/alphanum/alphanum.h"
#include "src/fluxengine.h"
#include <fstream>
@@ -18,28 +19,19 @@ int mainFluxfileLs(int argc, const char* argv[])
if (filenames.size() != 1)
error("you must specify exactly one filename");
const auto& filename = *filenames.begin();
fmt::print("Contents of {}:\n", filename);
FluxFileProto f = loadFl2File(filename);
fmt::print("version: {}\n", getProtoByString(&f, "version"));
fmt::print("rotational_period_ms: {}\n",
getProtoByString(&f, "rotational_period_ms"));
fmt::print("drive_type: {}\n", getProtoByString(&f, "drive_type"));
fmt::print("format_type: {}\n", getProtoByString(&f, "format_type"));
for (const auto& track : f.track())
for (const auto& filename : filenames)
{
for (int i = 0; i < track.flux().size(); i++)
{
const auto& flux = track.flux().at(i);
Fluxmap fluxmap(flux);
fmt::print("Contents of {}:\n", filename);
FluxFileProto f = loadFl2File(filename);
fmt::print("track.t{}_h{}.flux{}: {:.3f} ms, {} bytes\n",
track.track(),
track.head(),
i,
fluxmap.duration() / 1000000,
fluxmap.bytes());
auto fields = findAllProtoFields(f);
std::set<std::string, doj::alphanum_less<std::string>> fieldsSorted;
for (const auto& e : fields)
fieldsSorted.insert(e.first);
for (const auto& e : fieldsSorted)
{
fmt::print("{}: {}\n", e, getProtoByString(&f, e));
}
}

47
src/fe-fluxfilerm.cc Normal file
View File

@@ -0,0 +1,47 @@
#include "lib/core/globals.h"
#include "lib/config/flags.h"
#include "lib/data/fluxmap.h"
#include "lib/data/sector.h"
#include "lib/config/proto.h"
#include "lib/data/flux.h"
#include "lib/external/fl2.h"
#include "lib/external/fl2.pb.h"
#include "src/fluxengine.h"
#include <fstream>
static FlagGroup flags;
static std::string filename;
int mainFluxfileRm(int argc, const char* argv[])
{
const auto filenames = flags.parseFlagsWithFilenames(argc, argv);
if (filenames.size() != 1)
error("you must specify exactly one filename");
const auto& filename = *filenames.begin();
fmt::print("Contents of {}:\n", filename);
FluxFileProto f = loadFl2File(filename);
fmt::print("version: {}\n", getProtoByString(&f, "version"));
fmt::print("rotational_period_ms: {}\n",
getProtoByString(&f, "rotational_period_ms"));
fmt::print("drive_type: {}\n", getProtoByString(&f, "drive_type"));
fmt::print("format_type: {}\n", getProtoByString(&f, "format_type"));
for (const auto& track : f.track())
{
for (int i = 0; i < track.flux().size(); i++)
{
const auto& flux = track.flux().at(i);
Fluxmap fluxmap(flux);
fmt::print("track.t{}_h{}.flux{}: {:.3f} ms, {} bytes\n",
track.track(),
track.head(),
i,
fluxmap.duration() / 1000000,
fluxmap.bytes());
}
}
return 0;
}

View File

@@ -7,6 +7,7 @@ typedef int command_cb(int agrc, const char* argv[]);
extern command_cb mainAnalyseDriveResponse;
extern command_cb mainAnalyseLayout;
extern command_cb mainFluxfileLs;
extern command_cb mainFluxfileRm;
extern command_cb mainFormat;
extern command_cb mainGetDiskInfo;
extern command_cb mainGetFile;
@@ -80,6 +81,7 @@ static std::vector<Command> testables =
static std::vector<Command> fluxfileables =
{
{ "ls", mainFluxfileLs, "Lists the contents of a flux file.", },
{ "rm", mainFluxfileRm, "Removes flux from a flux file.", },
};
// clang-format on

0
src/fluxfile.cc Normal file
View File

4
src/fluxfile.h Normal file
View File

@@ -0,0 +1,4 @@
#pragma once
extern void parseFluxfilePath(
const std::string& combined, std::string& filename, std::string& path);

View File

@@ -57,6 +57,7 @@ protoencode(
name="formats_cc",
srcs={name: f"./{name}.textpb" for name in formats},
proto="ConfigProto",
include="lib/config/config.pb.h",
symbol="formats",
)

View File

@@ -26,6 +26,7 @@ protoencode(
name="drivetypes_cc",
srcs={name: f"./{name}.textpb" for name in drivetypes},
proto="ConfigProto",
include="lib/config/config.pb.h",
symbol="drivetypes",
)

View File

@@ -85,6 +85,9 @@ static void test_getting(void)
}
secondoption {
s: "2"
r: 0
r: 1
r: 2
}
range {
start: 1
@@ -108,6 +111,7 @@ static void test_getting(void)
AssertThat(getProtoByString(&tp, "r[1].s"), Equals("val3"));
AssertThrows(
ProtoPathNotFoundException, getProtoByString(&tp, "firstoption.s"));
AssertThat(getProtoByString(&tp, "secondoption.r[2]"), Equals("2"));
AssertThat(getProtoByString(&tp, "secondoption.s"), Equals("2"));
AssertThat(getProtoByString(&tp, "range"), Equals("1-3x2"));
}
@@ -223,15 +227,19 @@ static void test_fields(void)
Equals(std::vector<std::string>{"d",
"f",
"firstoption",
"firstoption.r[]",
"firstoption.s",
"i32",
"i64",
"m",
"m.r[]",
"m.s",
"r[]",
"r[].r[]",
"r[].s",
"range",
"secondoption",
"secondoption.r[]",
"secondoption.s",
"u32",
"u64"}));

View File

@@ -7,6 +7,7 @@ message TestProto
message SubMessageProto
{
optional string s = 1;
repeated int32 r = 2;
}
optional int64 i64 = 1 [(help) = "i64"];