mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-24 11:11:02 -07:00
Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
979b550178 | ||
|
|
9062a531f3 | ||
|
|
e2a6fbcf3c | ||
|
|
ec16931f3a | ||
|
|
0ec0ca7495 | ||
|
|
51fa7c9371 | ||
|
|
6c69f10fe7 | ||
|
|
206e85a356 | ||
|
|
8d7dd4867b | ||
|
|
d1524f78fb | ||
|
|
b26735d520 | ||
|
|
603baee777 | ||
|
|
e105b7f498 | ||
|
|
bb3fbccb50 | ||
|
|
c8edcd963d | ||
|
|
3b60cdc707 | ||
|
|
ea061d65c9 | ||
|
|
da64c0237f | ||
|
|
d2b1602881 | ||
|
|
1afd45068c | ||
|
|
f01b30e112 | ||
|
|
b5f7fbe14e | ||
|
|
996fdbc0f5 | ||
|
|
9ff3e3b42a | ||
|
|
0a5604521e | ||
|
|
0419df4b2d | ||
|
|
70bdcd0978 | ||
|
|
022df995aa | ||
|
|
dcbe7ec41d | ||
|
|
df4d27eefe | ||
|
|
8f233f55e9 |
6
.github/workflows/ccpp.yml
vendored
6
.github/workflows/ccpp.yml
vendored
@@ -65,12 +65,14 @@ jobs:
|
||||
run: |
|
||||
brew install sqlite pkg-config libusb protobuf wxwidgets fmt make coreutils dylibbundler libjpeg
|
||||
- name: make
|
||||
run: gmake -C fluxengine -j2
|
||||
run: gmake -C fluxengine
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ github.event.repository.name }}.${{ github.sha }}.fluxengine.${{ runner.arch }}.pkg
|
||||
path: fluxengine/FluxEngine.pkg
|
||||
path: |
|
||||
fluxengine/FluxEngine.pkg
|
||||
fluxengine/FluxEngine.app.zip
|
||||
|
||||
build-windows:
|
||||
runs-on: windows-latest
|
||||
|
||||
5
.github/workflows/release.yml
vendored
5
.github/workflows/release.yml
vendored
@@ -97,8 +97,9 @@ jobs:
|
||||
|
||||
- name: make
|
||||
run: |
|
||||
gmake -j2
|
||||
gmake
|
||||
mv FluxEngine.pkg FluxEngine-${{ runner.arch }}.pkg
|
||||
mv FluxEngine.app.zip FluxEngine-${{ runner.arch }}.app.zip
|
||||
|
||||
- name: tag
|
||||
uses: EndBug/latest-tag@latest
|
||||
@@ -115,6 +116,7 @@ jobs:
|
||||
tag: dev
|
||||
assets: |
|
||||
FluxEngine-${{ runner.arch }}.pkg
|
||||
FluxEngine-${{ runner.arch }}.app.zip
|
||||
fail-if-no-assets: false
|
||||
|
||||
- name: release
|
||||
@@ -123,6 +125,7 @@ jobs:
|
||||
name: Development build ${{ env.RELEASE_DATE }}
|
||||
files: |
|
||||
FluxEngine-${{ runner.arch }}.pkg
|
||||
FluxEngine-${{ runner.arch }}.app.zip
|
||||
tag_name: dev
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -263,6 +263,11 @@ As an exception, `dep/lexy` contains a partial copy of the lexy package, written
|
||||
by foonathen@github, taken from https://github.com/foonathan/lexy. It is BSL 1.0
|
||||
licensed. Please see the contents of the directory for the full text.
|
||||
|
||||
As an exception, `dep/alphanum` contains a copy of the alphanum package,
|
||||
written by Dave Koelle, taken from
|
||||
https://web.archive.org/web/20210207124255/davekoelle.com/alphanum.html. It is
|
||||
MIT licensed. Please see the source for the full text.
|
||||
|
||||
__Important:__ Because of all these exceptions, if you distribute the
|
||||
FluxEngine package as a whole, you must comply with the terms of _all_ of the
|
||||
licensing terms. This means that __effectively the FluxEngine package is
|
||||
|
||||
28
build.py
28
build.py
@@ -10,6 +10,7 @@ import re
|
||||
# Hack for building on Fedora/WSL; executables get the .exe extension,
|
||||
# build the build system detects it as Linux.
|
||||
import build.toolchain
|
||||
|
||||
toolchain.Toolchain.EXE = "$(EXT)"
|
||||
|
||||
package(name="protobuf_lib", package="protobuf")
|
||||
@@ -27,7 +28,7 @@ else:
|
||||
("acorndfs", "", "--200"),
|
||||
("agat", "", ""),
|
||||
("amiga", "", ""),
|
||||
("apple2", "", "--140 40track_drive"),
|
||||
("apple2", "", "--140 --drivetype=40"),
|
||||
("atarist", "", "--360"),
|
||||
("atarist", "", "--370"),
|
||||
("atarist", "", "--400"),
|
||||
@@ -37,17 +38,17 @@ else:
|
||||
("atarist", "", "--800"),
|
||||
("atarist", "", "--820"),
|
||||
("bk", "", ""),
|
||||
("brother", "", "--120 40track_drive"),
|
||||
("brother", "", "--120 --drivetype=40"),
|
||||
("brother", "", "--240"),
|
||||
(
|
||||
"commodore",
|
||||
"scripts/commodore1541_test.textpb",
|
||||
"--171 40track_drive",
|
||||
"--171 --drivetype=40",
|
||||
),
|
||||
(
|
||||
"commodore",
|
||||
"scripts/commodore1541_test.textpb",
|
||||
"--192 40track_drive",
|
||||
"--192 --drivetype=40",
|
||||
),
|
||||
("commodore", "", "--800"),
|
||||
("commodore", "", "--1620"),
|
||||
@@ -59,17 +60,17 @@ else:
|
||||
("ibm", "", "--1232"),
|
||||
("ibm", "", "--1440"),
|
||||
("ibm", "", "--1680"),
|
||||
("ibm", "", "--180 40track_drive"),
|
||||
("ibm", "", "--160 40track_drive"),
|
||||
("ibm", "", "--320 40track_drive"),
|
||||
("ibm", "", "--360 40track_drive"),
|
||||
("ibm", "", "--180 --drivetype=40"),
|
||||
("ibm", "", "--160 --drivetype=40"),
|
||||
("ibm", "", "--320 --drivetype=40"),
|
||||
("ibm", "", "--360 --drivetype=40"),
|
||||
("ibm", "", "--720_96"),
|
||||
("ibm", "", "--720_135"),
|
||||
("mac", "scripts/mac400_test.textpb", "--400"),
|
||||
("mac", "scripts/mac800_test.textpb", "--800"),
|
||||
("n88basic", "", ""),
|
||||
("rx50", "", ""),
|
||||
("tartu", "", "--390 40track_drive"),
|
||||
("tartu", "", "--390 --drivetype=40"),
|
||||
("tartu", "", "--780"),
|
||||
("tids990", "", ""),
|
||||
("victor9k", "", "--612"),
|
||||
@@ -108,6 +109,13 @@ export(
|
||||
"brother240tool$(EXT)": "tools+brother240tool",
|
||||
"upgrade-flux-file$(EXT)": "tools+upgrade-flux-file",
|
||||
}
|
||||
| ({"FluxEngine.pkg": "src/gui+fluxengine_pkg"} if config.osx else {}),
|
||||
| (
|
||||
{
|
||||
"FluxEngine.pkg": "src/gui+fluxengine_pkg",
|
||||
"FluxEngine.app.zip": "src/gui+fluxengine_app_zip",
|
||||
}
|
||||
if config.osx
|
||||
else {}
|
||||
),
|
||||
deps=["tests", "src/formats+docs", "scripts+mkdocindex"] + corpustests,
|
||||
)
|
||||
|
||||
@@ -87,7 +87,12 @@ $(PKG_CONFIG_HASHES) $(HOST_PKG_CONFIG_HASHES) &:
|
||||
|
||||
include $(OBJ)/build.mk
|
||||
|
||||
MAKEFLAGS += -r -j$(shell nproc)
|
||||
ifeq ($(OSX),yes)
|
||||
MAKEFLAGS += -r -j$(shell sysctl -n hw.logicalcpu)
|
||||
else
|
||||
MAKEFLAGS += -r -j$(shell nproc)
|
||||
endif
|
||||
|
||||
.DELETE_ON_ERROR:
|
||||
|
||||
.PHONY: update-ab
|
||||
|
||||
2
dep/alphanum/UPSTREAM.md
Normal file
2
dep/alphanum/UPSTREAM.md
Normal file
@@ -0,0 +1,2 @@
|
||||
Downloaded from:
|
||||
https://web.archive.org/web/20210918044134/http://davekoelle.com/files/alphanum.hpp
|
||||
450
dep/alphanum/alphanum.h
Normal file
450
dep/alphanum/alphanum.h
Normal file
@@ -0,0 +1,450 @@
|
||||
#ifndef ALPHANUM__HPP
|
||||
#define ALPHANUM__HPP
|
||||
|
||||
/*
|
||||
Released under the MIT License - https://opensource.org/licenses/MIT
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* $Header: /code/doj/alphanum.hpp,v 1.3 2008/01/28 23:06:47 doj Exp $ */
|
||||
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#ifdef ALPHANUM_LOCALE
|
||||
#include <cctype>
|
||||
#endif
|
||||
|
||||
#ifdef DOJDEBUG
|
||||
#include <iostream>
|
||||
#include <typeinfo>
|
||||
#endif
|
||||
|
||||
// TODO: make comparison with hexadecimal numbers. Extend the alphanum_comp() function by traits to choose between decimal and hexadecimal.
|
||||
|
||||
namespace doj
|
||||
{
|
||||
|
||||
// anonymous namespace for functions we use internally. But if you
|
||||
// are coding in C, you can use alphanum_impl() directly, since it
|
||||
// uses not C++ features.
|
||||
namespace {
|
||||
|
||||
// if you want to honour the locale settings for detecting digit
|
||||
// characters, you should define ALPHANUM_LOCALE
|
||||
#ifdef ALPHANUM_LOCALE
|
||||
/** wrapper function for ::isdigit() */
|
||||
bool alphanum_isdigit(int c)
|
||||
{
|
||||
return isdigit(c);
|
||||
}
|
||||
#else
|
||||
/** this function does not consider the current locale and only
|
||||
works with ASCII digits.
|
||||
@return true if c is a digit character
|
||||
*/
|
||||
bool alphanum_isdigit(const char c)
|
||||
{
|
||||
return c>='0' && c<='9';
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
compare l and r with strcmp() semantics, but using
|
||||
the "Alphanum Algorithm". This function is designed to read
|
||||
through the l and r strings only one time, for
|
||||
maximum performance. It does not allocate memory for
|
||||
substrings. It can either use the C-library functions isdigit()
|
||||
and atoi() to honour your locale settings, when recognizing
|
||||
digit characters when you "#define ALPHANUM_LOCALE=1" or use
|
||||
it's own digit character handling which only works with ASCII
|
||||
digit characters, but provides better performance.
|
||||
|
||||
@param l NULL-terminated C-style string
|
||||
@param r NULL-terminated C-style string
|
||||
@return negative if l<r, 0 if l equals r, positive if l>r
|
||||
*/
|
||||
int alphanum_impl(const char *l, const char *r)
|
||||
{
|
||||
enum mode_t { STRING, NUMBER } mode=STRING;
|
||||
|
||||
while(*l && *r)
|
||||
{
|
||||
if(mode == STRING)
|
||||
{
|
||||
char l_char, r_char;
|
||||
while((l_char=*l) && (r_char=*r))
|
||||
{
|
||||
// check if this are digit characters
|
||||
const bool l_digit=alphanum_isdigit(l_char), r_digit=alphanum_isdigit(r_char);
|
||||
// if both characters are digits, we continue in NUMBER mode
|
||||
if(l_digit && r_digit)
|
||||
{
|
||||
mode=NUMBER;
|
||||
break;
|
||||
}
|
||||
// if only the left character is a digit, we have a result
|
||||
if(l_digit) return -1;
|
||||
// if only the right character is a digit, we have a result
|
||||
if(r_digit) return +1;
|
||||
// compute the difference of both characters
|
||||
const int diff=l_char - r_char;
|
||||
// if they differ we have a result
|
||||
if(diff != 0) return diff;
|
||||
// otherwise process the next characters
|
||||
++l;
|
||||
++r;
|
||||
}
|
||||
}
|
||||
else // mode==NUMBER
|
||||
{
|
||||
#ifdef ALPHANUM_LOCALE
|
||||
// get the left number
|
||||
char *end;
|
||||
unsigned long l_int=strtoul(l, &end, 0);
|
||||
l=end;
|
||||
|
||||
// get the right number
|
||||
unsigned long r_int=strtoul(r, &end, 0);
|
||||
r=end;
|
||||
#else
|
||||
// get the left number
|
||||
unsigned long l_int=0;
|
||||
while(*l && alphanum_isdigit(*l))
|
||||
{
|
||||
// TODO: this can overflow
|
||||
l_int=l_int*10 + *l-'0';
|
||||
++l;
|
||||
}
|
||||
|
||||
// get the right number
|
||||
unsigned long r_int=0;
|
||||
while(*r && alphanum_isdigit(*r))
|
||||
{
|
||||
// TODO: this can overflow
|
||||
r_int=r_int*10 + *r-'0';
|
||||
++r;
|
||||
}
|
||||
#endif
|
||||
|
||||
// if the difference is not equal to zero, we have a comparison result
|
||||
const long diff=l_int-r_int;
|
||||
if(diff != 0)
|
||||
return diff;
|
||||
|
||||
// otherwise we process the next substring in STRING mode
|
||||
mode=STRING;
|
||||
}
|
||||
}
|
||||
|
||||
if(*r) return -1;
|
||||
if(*l) return +1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
Compare left and right with the same semantics as strcmp(), but with the
|
||||
"Alphanum Algorithm" which produces more human-friendly
|
||||
results. The classes lT and rT must implement "std::ostream
|
||||
operator<< (std::ostream&, const Ty&)".
|
||||
|
||||
@return negative if left<right, 0 if left==right, positive if left>right.
|
||||
*/
|
||||
template <typename lT, typename rT>
|
||||
int alphanum_comp(const lT& left, const rT& right)
|
||||
{
|
||||
#ifdef DOJDEBUG
|
||||
std::clog << "alphanum_comp<" << typeid(left).name() << "," << typeid(right).name() << "> " << left << "," << right << std::endl;
|
||||
#endif
|
||||
std::ostringstream l; l << left;
|
||||
std::ostringstream r; r << right;
|
||||
return alphanum_impl(l.str().c_str(), r.str().c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
Compare l and r with the same semantics as strcmp(), but with
|
||||
the "Alphanum Algorithm" which produces more human-friendly
|
||||
results.
|
||||
|
||||
@return negative if l<r, 0 if l==r, positive if l>r.
|
||||
*/
|
||||
template <>
|
||||
int alphanum_comp<std::string>(const std::string& l, const std::string& r)
|
||||
{
|
||||
#ifdef DOJDEBUG
|
||||
std::clog << "alphanum_comp<std::string,std::string> " << l << "," << r << std::endl;
|
||||
#endif
|
||||
return alphanum_impl(l.c_str(), r.c_str());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// now follow a lot of overloaded alphanum_comp() functions to get a
|
||||
// direct call to alphanum_impl() upon the various combinations of c
|
||||
// and c++ strings.
|
||||
|
||||
/**
|
||||
Compare l and r with the same semantics as strcmp(), but with
|
||||
the "Alphanum Algorithm" which produces more human-friendly
|
||||
results.
|
||||
|
||||
@return negative if l<r, 0 if l==r, positive if l>r.
|
||||
*/
|
||||
int alphanum_comp(char* l, char* r)
|
||||
{
|
||||
assert(l);
|
||||
assert(r);
|
||||
#ifdef DOJDEBUG
|
||||
std::clog << "alphanum_comp<char*,char*> " << l << "," << r << std::endl;
|
||||
#endif
|
||||
return alphanum_impl(l, r);
|
||||
}
|
||||
|
||||
int alphanum_comp(const char* l, const char* r)
|
||||
{
|
||||
assert(l);
|
||||
assert(r);
|
||||
#ifdef DOJDEBUG
|
||||
std::clog << "alphanum_comp<const char*,const char*> " << l << "," << r << std::endl;
|
||||
#endif
|
||||
return alphanum_impl(l, r);
|
||||
}
|
||||
|
||||
int alphanum_comp(char* l, const char* r)
|
||||
{
|
||||
assert(l);
|
||||
assert(r);
|
||||
#ifdef DOJDEBUG
|
||||
std::clog << "alphanum_comp<char*,const char*> " << l << "," << r << std::endl;
|
||||
#endif
|
||||
return alphanum_impl(l, r);
|
||||
}
|
||||
|
||||
int alphanum_comp(const char* l, char* r)
|
||||
{
|
||||
assert(l);
|
||||
assert(r);
|
||||
#ifdef DOJDEBUG
|
||||
std::clog << "alphanum_comp<const char*,char*> " << l << "," << r << std::endl;
|
||||
#endif
|
||||
return alphanum_impl(l, r);
|
||||
}
|
||||
|
||||
int alphanum_comp(const std::string& l, char* r)
|
||||
{
|
||||
assert(r);
|
||||
#ifdef DOJDEBUG
|
||||
std::clog << "alphanum_comp<std::string,char*> " << l << "," << r << std::endl;
|
||||
#endif
|
||||
return alphanum_impl(l.c_str(), r);
|
||||
}
|
||||
|
||||
int alphanum_comp(char* l, const std::string& r)
|
||||
{
|
||||
assert(l);
|
||||
#ifdef DOJDEBUG
|
||||
std::clog << "alphanum_comp<char*,std::string> " << l << "," << r << std::endl;
|
||||
#endif
|
||||
return alphanum_impl(l, r.c_str());
|
||||
}
|
||||
|
||||
int alphanum_comp(const std::string& l, const char* r)
|
||||
{
|
||||
assert(r);
|
||||
#ifdef DOJDEBUG
|
||||
std::clog << "alphanum_comp<std::string,const char*> " << l << "," << r << std::endl;
|
||||
#endif
|
||||
return alphanum_impl(l.c_str(), r);
|
||||
}
|
||||
|
||||
int alphanum_comp(const char* l, const std::string& r)
|
||||
{
|
||||
assert(l);
|
||||
#ifdef DOJDEBUG
|
||||
std::clog << "alphanum_comp<const char*,std::string> " << l << "," << r << std::endl;
|
||||
#endif
|
||||
return alphanum_impl(l, r.c_str());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
Functor class to compare two objects with the "Alphanum
|
||||
Algorithm". If the objects are no std::string, they must
|
||||
implement "std::ostream operator<< (std::ostream&, const Ty&)".
|
||||
*/
|
||||
template<class Ty>
|
||||
struct alphanum_less
|
||||
{
|
||||
bool operator()(const Ty& left, const Ty& right) const
|
||||
{
|
||||
return alphanum_comp(left, right) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#ifdef TESTMAIN
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
int main()
|
||||
{
|
||||
// testcases for the algorithm
|
||||
assert(doj::alphanum_comp("","") == 0);
|
||||
assert(doj::alphanum_comp("","a") < 0);
|
||||
assert(doj::alphanum_comp("a","") > 0);
|
||||
assert(doj::alphanum_comp("a","a") == 0);
|
||||
assert(doj::alphanum_comp("","9") < 0);
|
||||
assert(doj::alphanum_comp("9","") > 0);
|
||||
assert(doj::alphanum_comp("1","1") == 0);
|
||||
assert(doj::alphanum_comp("1","2") < 0);
|
||||
assert(doj::alphanum_comp("3","2") > 0);
|
||||
assert(doj::alphanum_comp("a1","a1") == 0);
|
||||
assert(doj::alphanum_comp("a1","a2") < 0);
|
||||
assert(doj::alphanum_comp("a2","a1") > 0);
|
||||
assert(doj::alphanum_comp("a1a2","a1a3") < 0);
|
||||
assert(doj::alphanum_comp("a1a2","a1a0") > 0);
|
||||
assert(doj::alphanum_comp("134","122") > 0);
|
||||
assert(doj::alphanum_comp("12a3","12a3") == 0);
|
||||
assert(doj::alphanum_comp("12a1","12a0") > 0);
|
||||
assert(doj::alphanum_comp("12a1","12a2") < 0);
|
||||
assert(doj::alphanum_comp("a","aa") < 0);
|
||||
assert(doj::alphanum_comp("aaa","aa") > 0);
|
||||
assert(doj::alphanum_comp("Alpha 2","Alpha 2") == 0);
|
||||
assert(doj::alphanum_comp("Alpha 2","Alpha 2A") < 0);
|
||||
assert(doj::alphanum_comp("Alpha 2 B","Alpha 2") > 0);
|
||||
|
||||
assert(doj::alphanum_comp(1,1) == 0);
|
||||
assert(doj::alphanum_comp(1,2) < 0);
|
||||
assert(doj::alphanum_comp(2,1) > 0);
|
||||
assert(doj::alphanum_comp(1.2,3.14) < 0);
|
||||
assert(doj::alphanum_comp(3.14,2.71) > 0);
|
||||
assert(doj::alphanum_comp(true,true) == 0);
|
||||
assert(doj::alphanum_comp(true,false) > 0);
|
||||
assert(doj::alphanum_comp(false,true) < 0);
|
||||
|
||||
std::string str("Alpha 2");
|
||||
assert(doj::alphanum_comp(str,"Alpha 2") == 0);
|
||||
assert(doj::alphanum_comp(str,"Alpha 2A") < 0);
|
||||
assert(doj::alphanum_comp("Alpha 2 B",str) > 0);
|
||||
|
||||
assert(doj::alphanum_comp(str,strdup("Alpha 2")) == 0);
|
||||
assert(doj::alphanum_comp(str,strdup("Alpha 2A")) < 0);
|
||||
assert(doj::alphanum_comp(strdup("Alpha 2 B"),str) > 0);
|
||||
|
||||
#if 1
|
||||
// show usage of the comparison functor with a set
|
||||
std::set<std::string, doj::alphanum_less<std::string> > s;
|
||||
s.insert("Xiph Xlater 58");
|
||||
s.insert("Xiph Xlater 5000");
|
||||
s.insert("Xiph Xlater 500");
|
||||
s.insert("Xiph Xlater 50");
|
||||
s.insert("Xiph Xlater 5");
|
||||
s.insert("Xiph Xlater 40");
|
||||
s.insert("Xiph Xlater 300");
|
||||
s.insert("Xiph Xlater 2000");
|
||||
s.insert("Xiph Xlater 10000");
|
||||
s.insert("QRS-62F Intrinsia Machine");
|
||||
s.insert("QRS-62 Intrinsia Machine");
|
||||
s.insert("QRS-60F Intrinsia Machine");
|
||||
s.insert("QRS-60 Intrinsia Machine");
|
||||
s.insert("Callisto Morphamax 7000 SE2");
|
||||
s.insert("Callisto Morphamax 7000 SE");
|
||||
s.insert("Callisto Morphamax 7000");
|
||||
s.insert("Callisto Morphamax 700");
|
||||
s.insert("Callisto Morphamax 600");
|
||||
s.insert("Callisto Morphamax 5000");
|
||||
s.insert("Callisto Morphamax 500");
|
||||
s.insert("Callisto Morphamax");
|
||||
s.insert("Alpha 2A-900");
|
||||
s.insert("Alpha 2A-8000");
|
||||
s.insert("Alpha 2A");
|
||||
s.insert("Alpha 200");
|
||||
s.insert("Alpha 2");
|
||||
s.insert("Alpha 100");
|
||||
s.insert("Allegia 60 Clasteron");
|
||||
s.insert("Allegia 52 Clasteron");
|
||||
s.insert("Allegia 51B Clasteron");
|
||||
s.insert("Allegia 51 Clasteron");
|
||||
s.insert("Allegia 500 Clasteron");
|
||||
s.insert("Allegia 50 Clasteron");
|
||||
s.insert("40X Radonius");
|
||||
s.insert("30X Radonius");
|
||||
s.insert("20X Radonius Prime");
|
||||
s.insert("20X Radonius");
|
||||
s.insert("200X Radonius");
|
||||
s.insert("10X Radonius");
|
||||
s.insert("1000X Radonius Maximus");
|
||||
// print sorted set to cout
|
||||
std::copy(s.begin(), s.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
|
||||
|
||||
// show usage of comparision functor with a map
|
||||
typedef std::map<std::string, int, doj::alphanum_less<std::string> > m_t;
|
||||
m_t m;
|
||||
m["z1.doc"]=1;
|
||||
m["z10.doc"]=2;
|
||||
m["z100.doc"]=3;
|
||||
m["z101.doc"]=4;
|
||||
m["z102.doc"]=5;
|
||||
m["z11.doc"]=6;
|
||||
m["z12.doc"]=7;
|
||||
m["z13.doc"]=8;
|
||||
m["z14.doc"]=9;
|
||||
m["z15.doc"]=10;
|
||||
m["z16.doc"]=11;
|
||||
m["z17.doc"]=12;
|
||||
m["z18.doc"]=13;
|
||||
m["z19.doc"]=14;
|
||||
m["z2.doc"]=15;
|
||||
m["z20.doc"]=16;
|
||||
m["z3.doc"]=17;
|
||||
m["z4.doc"]=18;
|
||||
m["z5.doc"]=19;
|
||||
m["z6.doc"]=20;
|
||||
m["z7.doc"]=21;
|
||||
m["z8.doc"]=22;
|
||||
m["z9.doc"]=23;
|
||||
// print sorted map to cout
|
||||
for(m_t::iterator i=m.begin(); i!=m.end(); ++i)
|
||||
std::cout << i->first << '\t' << i->second << std::endl;
|
||||
|
||||
// show usage of comparison functor with an STL algorithm on a vector
|
||||
std::vector<std::string> v;
|
||||
// vector contents are reversed sorted contents of the old set
|
||||
std::copy(s.rbegin(), s.rend(), std::back_inserter(v));
|
||||
// now sort the vector with the algorithm
|
||||
std::sort(v.begin(), v.end(), doj::alphanum_less<std::string>());
|
||||
// and print the vector to cout
|
||||
std::copy(v.begin(), v.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
8
dep/alphanum/build.py
Normal file
8
dep/alphanum/build.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from build.c import clibrary
|
||||
|
||||
clibrary(
|
||||
name="alphanum",
|
||||
srcs=[],
|
||||
hdrs={"dep/alphanum/alphanum.h": "./alphanum.h"},
|
||||
)
|
||||
|
||||
@@ -31,9 +31,9 @@ they might require nudging as the side order can't be reliably autodetected.
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read acornadfs --160 -s drive:0 -o acornadfs.img`
|
||||
- `fluxengine read acornadfs --320 -s drive:0 -o acornadfs.img`
|
||||
- `fluxengine read acornadfs --640 -s drive:0 -o acornadfs.img`
|
||||
- `fluxengine read acornadfs --800 -s drive:0 -o acornadfs.img`
|
||||
- `fluxengine read acornadfs --1600 -s drive:0 -o acornadfs.img`
|
||||
- `fluxengine read -c acornadfs --160 -s drive:0 -o acornadfs.img`
|
||||
- `fluxengine read -c acornadfs --320 -s drive:0 -o acornadfs.img`
|
||||
- `fluxengine read -c acornadfs --640 -s drive:0 -o acornadfs.img`
|
||||
- `fluxengine read -c acornadfs --800 -s drive:0 -o acornadfs.img`
|
||||
- `fluxengine read -c acornadfs --1600 -s drive:0 -o acornadfs.img`
|
||||
|
||||
|
||||
@@ -24,13 +24,13 @@ requires a bit of fiddling as they have the same tracks on twice.
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read acorndfs --100 -s drive:0 -o acorndfs.img`
|
||||
- `fluxengine read acorndfs --200 -s drive:0 -o acorndfs.img`
|
||||
- `fluxengine read -c acorndfs --100 -s drive:0 -o acorndfs.img`
|
||||
- `fluxengine read -c acorndfs --200 -s drive:0 -o acorndfs.img`
|
||||
|
||||
To write:
|
||||
|
||||
- `fluxengine write acorndfs --100 -d drive:0 -i acorndfs.img`
|
||||
- `fluxengine write acorndfs --200 -d drive:0 -i acorndfs.img`
|
||||
- `fluxengine write -c acorndfs --100 -d drive:0 -i acorndfs.img`
|
||||
- `fluxengine write -c acorndfs --200 -d drive:0 -i acorndfs.img`
|
||||
|
||||
## References
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ based on what looks right. If anyone knows _anything_ about these disks,
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read aeslanier -s drive:0 -o aeslanier.img`
|
||||
- `fluxengine read -c aeslanier -s drive:0 -o aeslanier.img`
|
||||
|
||||
## References
|
||||
|
||||
|
||||
@@ -20,11 +20,11 @@ profile.
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read agat -s drive:0 -o agat.img`
|
||||
- `fluxengine read -c agat -s drive:0 -o agat.img`
|
||||
|
||||
To write:
|
||||
|
||||
- `fluxengine write agat -d drive:0 -i agat.img`
|
||||
- `fluxengine write -c agat -d drive:0 -i agat.img`
|
||||
|
||||
## References
|
||||
|
||||
|
||||
@@ -26,11 +26,11 @@ distinctly subpar and not particularly good at detecting errors.
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read amiga -s drive:0 -o amiga.adf`
|
||||
- `fluxengine read -c amiga -s drive:0 -o amiga.adf`
|
||||
|
||||
To write:
|
||||
|
||||
- `fluxengine write amiga -d drive:0 -i amiga.adf`
|
||||
- `fluxengine write -c amiga -d drive:0 -i amiga.adf`
|
||||
|
||||
## References
|
||||
|
||||
|
||||
@@ -43,8 +43,8 @@ kayinfo.lbr
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read ampro --400 -s drive:0 -o ampro.img`
|
||||
- `fluxengine read ampro --800 -s drive:0 -o ampro.img`
|
||||
- `fluxengine read -c ampro --400 -s drive:0 -o ampro.img`
|
||||
- `fluxengine read -c ampro --800 -s drive:0 -o ampro.img`
|
||||
|
||||
## References
|
||||
|
||||
|
||||
@@ -58,13 +58,13 @@ volume.
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read apple2 --140 -s drive:0 -o apple2.img`
|
||||
- `fluxengine read apple2 --640 -s drive:0 -o apple2.img`
|
||||
- `fluxengine read -c apple2 --140 -s drive:0 -o apple2.img`
|
||||
- `fluxengine read -c apple2 --640 -s drive:0 -o apple2.img`
|
||||
|
||||
To write:
|
||||
|
||||
- `fluxengine write apple2 --140 -d drive:0 -i apple2.img`
|
||||
- `fluxengine write apple2 --640 -d drive:0 -i apple2.img`
|
||||
- `fluxengine write -c apple2 --140 -d drive:0 -i apple2.img`
|
||||
- `fluxengine write -c apple2 --640 -d drive:0 -i apple2.img`
|
||||
|
||||
## References
|
||||
|
||||
|
||||
@@ -29,25 +29,25 @@ Be aware that many PC drives (including mine) won't do the 82 track formats.
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read atarist --360 -s drive:0 -o atarist.img`
|
||||
- `fluxengine read atarist --370 -s drive:0 -o atarist.img`
|
||||
- `fluxengine read atarist --400 -s drive:0 -o atarist.img`
|
||||
- `fluxengine read atarist --410 -s drive:0 -o atarist.img`
|
||||
- `fluxengine read atarist --720 -s drive:0 -o atarist.img`
|
||||
- `fluxengine read atarist --740 -s drive:0 -o atarist.img`
|
||||
- `fluxengine read atarist --800 -s drive:0 -o atarist.img`
|
||||
- `fluxengine read atarist --820 -s drive:0 -o atarist.img`
|
||||
- `fluxengine read -c atarist --360 -s drive:0 -o atarist.img`
|
||||
- `fluxengine read -c atarist --370 -s drive:0 -o atarist.img`
|
||||
- `fluxengine read -c atarist --400 -s drive:0 -o atarist.img`
|
||||
- `fluxengine read -c atarist --410 -s drive:0 -o atarist.img`
|
||||
- `fluxengine read -c atarist --720 -s drive:0 -o atarist.img`
|
||||
- `fluxengine read -c atarist --740 -s drive:0 -o atarist.img`
|
||||
- `fluxengine read -c atarist --800 -s drive:0 -o atarist.img`
|
||||
- `fluxengine read -c atarist --820 -s drive:0 -o atarist.img`
|
||||
|
||||
To write:
|
||||
|
||||
- `fluxengine write atarist --360 -d drive:0 -i atarist.img`
|
||||
- `fluxengine write atarist --370 -d drive:0 -i atarist.img`
|
||||
- `fluxengine write atarist --400 -d drive:0 -i atarist.img`
|
||||
- `fluxengine write atarist --410 -d drive:0 -i atarist.img`
|
||||
- `fluxengine write atarist --720 -d drive:0 -i atarist.img`
|
||||
- `fluxengine write atarist --740 -d drive:0 -i atarist.img`
|
||||
- `fluxengine write atarist --800 -d drive:0 -i atarist.img`
|
||||
- `fluxengine write atarist --820 -d drive:0 -i atarist.img`
|
||||
- `fluxengine write -c atarist --360 -d drive:0 -i atarist.img`
|
||||
- `fluxengine write -c atarist --370 -d drive:0 -i atarist.img`
|
||||
- `fluxengine write -c atarist --400 -d drive:0 -i atarist.img`
|
||||
- `fluxengine write -c atarist --410 -d drive:0 -i atarist.img`
|
||||
- `fluxengine write -c atarist --720 -d drive:0 -i atarist.img`
|
||||
- `fluxengine write -c atarist --740 -d drive:0 -i atarist.img`
|
||||
- `fluxengine write -c atarist --800 -d drive:0 -i atarist.img`
|
||||
- `fluxengine write -c atarist --820 -d drive:0 -i atarist.img`
|
||||
|
||||
## References
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@ on what was available at the time, with the same format on both.
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read bk -s drive:0 -o bk800.img`
|
||||
- `fluxengine read -c bk -s drive:0 -o bk800.img`
|
||||
|
||||
To write:
|
||||
|
||||
- `fluxengine write bk -d drive:0 -i bk800.img`
|
||||
- `fluxengine write -c bk -d drive:0 -i bk800.img`
|
||||
|
||||
|
||||
@@ -44,13 +44,13 @@ investigate.
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read brother --120 -s drive:0 -o brother.img`
|
||||
- `fluxengine read brother --240 -s drive:0 -o brother.img`
|
||||
- `fluxengine read -c brother --120 -s drive:0 -o brother.img`
|
||||
- `fluxengine read -c brother --240 -s drive:0 -o brother.img`
|
||||
|
||||
To write:
|
||||
|
||||
- `fluxengine write brother --120 -d drive:0 -i brother.img`
|
||||
- `fluxengine write brother --240 -d drive:0 -i brother.img`
|
||||
- `fluxengine write -c brother --120 -d drive:0 -i brother.img`
|
||||
- `fluxengine write -c brother --240 -d drive:0 -i brother.img`
|
||||
|
||||
Dealing with misaligned disks
|
||||
-----------------------------
|
||||
|
||||
@@ -54,18 +54,18 @@ A CMD FD2000 disk (a popular third-party Commodore disk drive)
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read commodore --171 -s drive:0 -o commodore.d64`
|
||||
- `fluxengine read commodore --192 -s drive:0 -o commodore.d64`
|
||||
- `fluxengine read commodore --800 -s drive:0 -o commodore.d64`
|
||||
- `fluxengine read commodore --1042 -s drive:0 -o commodore.d64`
|
||||
- `fluxengine read commodore --1620 -s drive:0 -o commodore.d64`
|
||||
- `fluxengine read -c commodore --171 -s drive:0 -o commodore.d64`
|
||||
- `fluxengine read -c commodore --192 -s drive:0 -o commodore.d64`
|
||||
- `fluxengine read -c commodore --800 -s drive:0 -o commodore.d64`
|
||||
- `fluxengine read -c commodore --1042 -s drive:0 -o commodore.d64`
|
||||
- `fluxengine read -c commodore --1620 -s drive:0 -o commodore.d64`
|
||||
|
||||
To write:
|
||||
|
||||
- `fluxengine write commodore --171 -d drive:0 -i commodore.d64`
|
||||
- `fluxengine write commodore --192 -d drive:0 -i commodore.d64`
|
||||
- `fluxengine write commodore --800 -d drive:0 -i commodore.d64`
|
||||
- `fluxengine write commodore --1620 -d drive:0 -i commodore.d64`
|
||||
- `fluxengine write -c commodore --171 -d drive:0 -i commodore.d64`
|
||||
- `fluxengine write -c commodore --192 -d drive:0 -i commodore.d64`
|
||||
- `fluxengine write -c commodore --800 -d drive:0 -i commodore.d64`
|
||||
- `fluxengine write -c commodore --1620 -d drive:0 -i commodore.d64`
|
||||
|
||||
## References
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ images.
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read eco1 -s drive:0 -o eco1.img`
|
||||
- `fluxengine read -c eco1 -s drive:0 -o eco1.img`
|
||||
|
||||
## References
|
||||
|
||||
|
||||
@@ -15,5 +15,5 @@ format itself is yet another IBM scheme variant.
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read epsonpf10 -s drive:0 -o epsonpf10.img`
|
||||
- `fluxengine read -c epsonpf10 -s drive:0 -o epsonpf10.img`
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ touch](https://github.com/davidgiven/fluxengine/issues/new).
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read f85 -s drive:0 -o f85.img`
|
||||
- `fluxengine read -c f85 -s drive:0 -o f85.img`
|
||||
|
||||
## References
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ I don't have access to one of those disks.
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read fb100 -s drive:0 -o fb100.img`
|
||||
- `fluxengine read -c fb100 -s drive:0 -o fb100.img`
|
||||
|
||||
## References
|
||||
|
||||
|
||||
@@ -23,17 +23,17 @@ encoding scheme.
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read hplif --264 -s drive:0 -o hplif.img`
|
||||
- `fluxengine read hplif --608 -s drive:0 -o hplif.img`
|
||||
- `fluxengine read hplif --616 -s drive:0 -o hplif.img`
|
||||
- `fluxengine read hplif --770 -s drive:0 -o hplif.img`
|
||||
- `fluxengine read -c hplif --264 -s drive:0 -o hplif.img`
|
||||
- `fluxengine read -c hplif --608 -s drive:0 -o hplif.img`
|
||||
- `fluxengine read -c hplif --616 -s drive:0 -o hplif.img`
|
||||
- `fluxengine read -c hplif --770 -s drive:0 -o hplif.img`
|
||||
|
||||
To write:
|
||||
|
||||
- `fluxengine write hplif --264 -d drive:0 -i hplif.img`
|
||||
- `fluxengine write hplif --608 -d drive:0 -i hplif.img`
|
||||
- `fluxengine write hplif --616 -d drive:0 -i hplif.img`
|
||||
- `fluxengine write hplif --770 -d drive:0 -i hplif.img`
|
||||
- `fluxengine write -c hplif --264 -d drive:0 -i hplif.img`
|
||||
- `fluxengine write -c hplif --608 -d drive:0 -i hplif.img`
|
||||
- `fluxengine write -c hplif --616 -d drive:0 -i hplif.img`
|
||||
- `fluxengine write -c hplif --770 -d drive:0 -i hplif.img`
|
||||
|
||||
## References
|
||||
|
||||
|
||||
@@ -55,30 +55,30 @@ image format. FluxEngine will use these parameters.
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read ibm --auto -s drive:0 -o ibm.img`
|
||||
- `fluxengine read ibm --160 -s drive:0 -o ibm.img`
|
||||
- `fluxengine read ibm --180 -s drive:0 -o ibm.img`
|
||||
- `fluxengine read ibm --320 -s drive:0 -o ibm.img`
|
||||
- `fluxengine read ibm --360 -s drive:0 -o ibm.img`
|
||||
- `fluxengine read ibm --720_96 -s drive:0 -o ibm.img`
|
||||
- `fluxengine read ibm --720_135 -s drive:0 -o ibm.img`
|
||||
- `fluxengine read ibm --1200 -s drive:0 -o ibm.img`
|
||||
- `fluxengine read ibm --1232 -s drive:0 -o ibm.img`
|
||||
- `fluxengine read ibm --1440 -s drive:0 -o ibm.img`
|
||||
- `fluxengine read ibm --1680 -s drive:0 -o ibm.img`
|
||||
- `fluxengine read -c ibm --auto -s drive:0 -o ibm.img`
|
||||
- `fluxengine read -c ibm --160 -s drive:0 -o ibm.img`
|
||||
- `fluxengine read -c ibm --180 -s drive:0 -o ibm.img`
|
||||
- `fluxengine read -c ibm --320 -s drive:0 -o ibm.img`
|
||||
- `fluxengine read -c ibm --360 -s drive:0 -o ibm.img`
|
||||
- `fluxengine read -c ibm --720_96 -s drive:0 -o ibm.img`
|
||||
- `fluxengine read -c ibm --720_135 -s drive:0 -o ibm.img`
|
||||
- `fluxengine read -c ibm --1200 -s drive:0 -o ibm.img`
|
||||
- `fluxengine read -c ibm --1232 -s drive:0 -o ibm.img`
|
||||
- `fluxengine read -c ibm --1440 -s drive:0 -o ibm.img`
|
||||
- `fluxengine read -c ibm --1680 -s drive:0 -o ibm.img`
|
||||
|
||||
To write:
|
||||
|
||||
- `fluxengine write ibm --160 -d drive:0 -i ibm.img`
|
||||
- `fluxengine write ibm --180 -d drive:0 -i ibm.img`
|
||||
- `fluxengine write ibm --320 -d drive:0 -i ibm.img`
|
||||
- `fluxengine write ibm --360 -d drive:0 -i ibm.img`
|
||||
- `fluxengine write ibm --720_96 -d drive:0 -i ibm.img`
|
||||
- `fluxengine write ibm --720_135 -d drive:0 -i ibm.img`
|
||||
- `fluxengine write ibm --1200 -d drive:0 -i ibm.img`
|
||||
- `fluxengine write ibm --1232 -d drive:0 -i ibm.img`
|
||||
- `fluxengine write ibm --1440 -d drive:0 -i ibm.img`
|
||||
- `fluxengine write ibm --1680 -d drive:0 -i ibm.img`
|
||||
- `fluxengine write -c ibm --160 -d drive:0 -i ibm.img`
|
||||
- `fluxengine write -c ibm --180 -d drive:0 -i ibm.img`
|
||||
- `fluxengine write -c ibm --320 -d drive:0 -i ibm.img`
|
||||
- `fluxengine write -c ibm --360 -d drive:0 -i ibm.img`
|
||||
- `fluxengine write -c ibm --720_96 -d drive:0 -i ibm.img`
|
||||
- `fluxengine write -c ibm --720_135 -d drive:0 -i ibm.img`
|
||||
- `fluxengine write -c ibm --1200 -d drive:0 -i ibm.img`
|
||||
- `fluxengine write -c ibm --1232 -d drive:0 -i ibm.img`
|
||||
- `fluxengine write -c ibm --1440 -d drive:0 -i ibm.img`
|
||||
- `fluxengine write -c ibm --1680 -d drive:0 -i ibm.img`
|
||||
|
||||
Mixed-format disks
|
||||
------------------
|
||||
|
||||
@@ -15,5 +15,5 @@ track! Other than that it's another IBM scheme variation.
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read icl30 -s drive:0 -o icl30.img`
|
||||
- `fluxengine read -c icl30 -s drive:0 -o icl30.img`
|
||||
|
||||
|
||||
@@ -47,13 +47,13 @@ standard for disk images is to omit it. If you want them, specify that you want
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read mac --400 -s drive:0 -o mac.dsk`
|
||||
- `fluxengine read mac --800 -s drive:0 -o mac.dsk`
|
||||
- `fluxengine read -c mac --400 -s drive:0 -o mac.dsk`
|
||||
- `fluxengine read -c mac --800 -s drive:0 -o mac.dsk`
|
||||
|
||||
To write:
|
||||
|
||||
- `fluxengine write mac --400 -d drive:0 -i mac.dsk`
|
||||
- `fluxengine write mac --800 -d drive:0 -i mac.dsk`
|
||||
- `fluxengine write -c mac --400 -d drive:0 -i mac.dsk`
|
||||
- `fluxengine write -c mac --800 -d drive:0 -i mac.dsk`
|
||||
|
||||
## References
|
||||
|
||||
|
||||
@@ -63,11 +63,11 @@ need to apply extra options to change the format if desired.
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read micropolis -s drive:0 -o micropolis.img`
|
||||
- `fluxengine read -c micropolis -s drive:0 -o micropolis.img`
|
||||
|
||||
To write:
|
||||
|
||||
- `fluxengine write micropolis -d drive:0 -i micropolis.img`
|
||||
- `fluxengine write -c micropolis -d drive:0 -i micropolis.img`
|
||||
|
||||
## References
|
||||
|
||||
|
||||
@@ -52,10 +52,10 @@ Words are all stored little-endian.
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read mx --110 -s drive:0 -o mx.img`
|
||||
- `fluxengine read mx --220ds -s drive:0 -o mx.img`
|
||||
- `fluxengine read mx --220ss -s drive:0 -o mx.img`
|
||||
- `fluxengine read mx --440 -s drive:0 -o mx.img`
|
||||
- `fluxengine read -c mx --110 -s drive:0 -o mx.img`
|
||||
- `fluxengine read -c mx --220ds -s drive:0 -o mx.img`
|
||||
- `fluxengine read -c mx --220ss -s drive:0 -o mx.img`
|
||||
- `fluxengine read -c mx --440 -s drive:0 -o mx.img`
|
||||
|
||||
## References
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@ boot ROM could only read single density data.)
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read n88basic -s drive:0 -o n88basic.img`
|
||||
- `fluxengine read -c n88basic -s drive:0 -o n88basic.img`
|
||||
|
||||
To write:
|
||||
|
||||
- `fluxengine write n88basic -d drive:0 -i n88basic.img`
|
||||
- `fluxengine write -c n88basic -d drive:0 -i n88basic.img`
|
||||
|
||||
|
||||
@@ -31,15 +31,15 @@ equivalent to .img images.
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read northstar --87 -s drive:0 -o northstar.nsi`
|
||||
- `fluxengine read northstar --175 -s drive:0 -o northstar.nsi`
|
||||
- `fluxengine read northstar --350 -s drive:0 -o northstar.nsi`
|
||||
- `fluxengine read -c northstar --87 -s drive:0 -o northstar.nsi`
|
||||
- `fluxengine read -c northstar --175 -s drive:0 -o northstar.nsi`
|
||||
- `fluxengine read -c northstar --350 -s drive:0 -o northstar.nsi`
|
||||
|
||||
To write:
|
||||
|
||||
- `fluxengine write northstar --87 -d drive:0 -i northstar.nsi`
|
||||
- `fluxengine write northstar --175 -d drive:0 -i northstar.nsi`
|
||||
- `fluxengine write northstar --350 -d drive:0 -i northstar.nsi`
|
||||
- `fluxengine write -c northstar --87 -d drive:0 -i northstar.nsi`
|
||||
- `fluxengine write -c northstar --175 -d drive:0 -i northstar.nsi`
|
||||
- `fluxengine write -c northstar --350 -d drive:0 -i northstar.nsi`
|
||||
|
||||
## References
|
||||
|
||||
|
||||
@@ -24,9 +24,9 @@ and, oddly, swapped sides.
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read psos -s drive:0 -o pme.img`
|
||||
- `fluxengine read -c psos -s drive:0 -o pme.img`
|
||||
|
||||
To write:
|
||||
|
||||
- `fluxengine write psos -d drive:0 -i pme.img`
|
||||
- `fluxengine write -c psos -d drive:0 -i pme.img`
|
||||
|
||||
|
||||
@@ -40,9 +40,9 @@ for assistance with this!
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read rolandd20 -s drive:0 -o rolandd20.img`
|
||||
- `fluxengine read -c rolandd20 -s drive:0 -o rolandd20.img`
|
||||
|
||||
To write:
|
||||
|
||||
- `fluxengine write rolandd20 -d drive:0 -i rolandd20.img`
|
||||
- `fluxengine write -c rolandd20 -d drive:0 -i rolandd20.img`
|
||||
|
||||
|
||||
@@ -15,9 +15,9 @@ vanilla single-sided IBM scheme variation.
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read rx50 -s drive:0 -o rx50.img`
|
||||
- `fluxengine read -c rx50 -s drive:0 -o rx50.img`
|
||||
|
||||
To write:
|
||||
|
||||
- `fluxengine write rx50 -d drive:0 -i rx50.img`
|
||||
- `fluxengine write -c rx50 -d drive:0 -i rx50.img`
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ this is completely correct, so don't trust it!
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read smaky6 -s drive:0 -o smaky6.img`
|
||||
- `fluxengine read -c smaky6 -s drive:0 -o smaky6.img`
|
||||
|
||||
## References
|
||||
|
||||
|
||||
@@ -34,13 +34,13 @@ FluxEngine supports reading and writing Tartu disks with CP/M filesystem access.
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read tartu --390 -s drive:0 -o tartu.img`
|
||||
- `fluxengine read tartu --780 -s drive:0 -o tartu.img`
|
||||
- `fluxengine read -c tartu --390 -s drive:0 -o tartu.img`
|
||||
- `fluxengine read -c tartu --780 -s drive:0 -o tartu.img`
|
||||
|
||||
To write:
|
||||
|
||||
- `fluxengine write tartu --390 -d drive:0 -i tartu.img`
|
||||
- `fluxengine write tartu --780 -d drive:0 -i tartu.img`
|
||||
- `fluxengine write -c tartu --390 -d drive:0 -i tartu.img`
|
||||
- `fluxengine write -c tartu --780 -d drive:0 -i tartu.img`
|
||||
|
||||
## References
|
||||
|
||||
|
||||
@@ -26,11 +26,11 @@ FluxEngine will read and write these (but only the DSDD MFM variant).
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read tids990 -s drive:0 -o tids990.img`
|
||||
- `fluxengine read -c tids990 -s drive:0 -o tids990.img`
|
||||
|
||||
To write:
|
||||
|
||||
- `fluxengine write tids990 -d drive:0 -i tids990.img`
|
||||
- `fluxengine write -c tids990 -d drive:0 -i tids990.img`
|
||||
|
||||
## References
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@ on the precise format.
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read tiki --90 -s drive:0 -o tiki.img`
|
||||
- `fluxengine read tiki --200 -s drive:0 -o tiki.img`
|
||||
- `fluxengine read tiki --400 -s drive:0 -o tiki.img`
|
||||
- `fluxengine read tiki --800 -s drive:0 -o tiki.img`
|
||||
- `fluxengine read -c tiki --90 -s drive:0 -o tiki.img`
|
||||
- `fluxengine read -c tiki --200 -s drive:0 -o tiki.img`
|
||||
- `fluxengine read -c tiki --400 -s drive:0 -o tiki.img`
|
||||
- `fluxengine read -c tiki --800 -s drive:0 -o tiki.img`
|
||||
|
||||
|
||||
@@ -46,13 +46,13 @@ FluxEngine can read and write both the single-sided and double-sided variants.
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read victor9k --612 -s drive:0 -o victor9k.img`
|
||||
- `fluxengine read victor9k --1224 -s drive:0 -o victor9k.img`
|
||||
- `fluxengine read -c victor9k --612 -s drive:0 -o victor9k.img`
|
||||
- `fluxengine read -c victor9k --1224 -s drive:0 -o victor9k.img`
|
||||
|
||||
To write:
|
||||
|
||||
- `fluxengine write victor9k --612 -d drive:0 -i victor9k.img`
|
||||
- `fluxengine write victor9k --1224 -d drive:0 -i victor9k.img`
|
||||
- `fluxengine write -c victor9k --612 -d drive:0 -i victor9k.img`
|
||||
- `fluxengine write -c victor9k --1224 -d drive:0 -i victor9k.img`
|
||||
|
||||
## References
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ system.
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read zilogmcz -s drive:0 -o zilogmcz.img`
|
||||
- `fluxengine read -c zilogmcz -s drive:0 -o zilogmcz.img`
|
||||
|
||||
## References
|
||||
|
||||
|
||||
39
doc/using.md
39
doc/using.md
@@ -15,7 +15,7 @@ If possible, try using the GUI, which should provide simplified access for most
|
||||
common operations.
|
||||
|
||||
<div style="text-align: center">
|
||||
<a href="doc/screenshot-details.png"><img src="doc/screenshot-details.png" style="width:60%" alt="screenshot of the GUI in action"></a>
|
||||
<a href="screenshot-details.png"><img src="screenshot-details.png" style="width:60%" alt="screenshot of the GUI in action"></a>
|
||||
</div>
|
||||
|
||||
### Core concepts
|
||||
@@ -82,16 +82,16 @@ Here are some sample invocations:
|
||||
```
|
||||
# Read an PC 1440kB disk, producing a disk image with the default name
|
||||
# (ibm.img)
|
||||
$ fluxengine read ibm --1440
|
||||
$ fluxengine read -c ibm --1440
|
||||
|
||||
# Write a PC 1440kB disk to drive 1
|
||||
$ fluxengine write ibm --1440 -i image.img -d drive:1
|
||||
$ fluxengine write -c ibm --1440 -i image.img -d drive:1
|
||||
|
||||
# Read a Eco1 CP/M disk, making a copy of the flux into a file
|
||||
$ fluxengine read eco1 --copy-flux-to copy.flux -o eco1.ldbs
|
||||
$ fluxengine read -c eco1 --copy-flux-to copy.flux -o eco1.ldbs
|
||||
|
||||
# Rerun the decode from the flux file, tweaking the parameters
|
||||
$ fluxengine read eco1 -s copy.flux -o eco1.ldbs --cylinders=1
|
||||
$ fluxengine read -c eco1 -s copy.flux -o eco1.ldbs --cylinders=1
|
||||
```
|
||||
|
||||
### Configuration
|
||||
@@ -146,14 +146,14 @@ different task. Run each one with `--help` to get a full list of
|
||||
(non-configuration-setting) options; this describes only basic usage of the
|
||||
more common tools.
|
||||
|
||||
- `fluxengine read <profile> <options> -s <flux source> -o <image output>`
|
||||
- `fluxengine read -c <profile> <options> -s <flux source> -o <image output>`
|
||||
|
||||
Reads flux (possibly from a disk) and decodes it into a file system image.
|
||||
`<profile>` is a reference to an internal input configuration file
|
||||
describing the format. `<options>` may be any combination of options
|
||||
defined by the profile.
|
||||
|
||||
- `fluxengine write <profile> -i <image input> -d <flux destination>`
|
||||
- `fluxengine write -c <profile> -i <image input> -d <flux destination>`
|
||||
|
||||
Reads a filesystem image and encodes it into flux (possibly writing to a
|
||||
disk). `<profile>` is a reference to an internal output configuration file
|
||||
@@ -377,6 +377,27 @@ FluxEngine also supports a number of file system image formats. When using the
|
||||
correctness. Individual records are separated by three `\\0` bytes and tracks
|
||||
are separated by four `\\0` bytes; tracks are emitted in CHS order.
|
||||
|
||||
### Manipulating flux files
|
||||
|
||||
`fluxengine fluxfile` contains a set of tools for examining or manipulating
|
||||
individual flux files. (Note that this only works on flux files themselves, not
|
||||
sources.)
|
||||
|
||||
- `fluxfile ls -f <fluxfile>`
|
||||
|
||||
Shows all the components inside a flux file.
|
||||
|
||||
- `fluxfile rm -f <fluxfile> -t <tracks>`
|
||||
|
||||
Removes flux from a flux file. All reads are removed from the specified track;
|
||||
use the normal `c0h0` syntax, including using ranges, such as `c0-9h0-1`.
|
||||
|
||||
- `fluxfile cp -i <input fluxfile> -o <output fluxfile> -t <tracks>`
|
||||
|
||||
Copies flux from one file to another. All reads are copied from the source
|
||||
file and _appended_ to the relevant track in the destination file. You can use
|
||||
ranges etc.
|
||||
|
||||
### High density disks
|
||||
|
||||
High density disks use a different magnetic medium to low and double density
|
||||
@@ -468,7 +489,7 @@ containing valuable historical data, and you want to read them.
|
||||
Typically I do this:
|
||||
|
||||
```
|
||||
$ fluxengine read brother240 -s drive:0 -o brother.img --copy-flux-to=brother.flux --decoder.write_csv_to=brother.csv
|
||||
$ fluxengine read -c brother240 -s drive:0 -o brother.img --copy-flux-to=brother.flux --decoder.write_csv_to=brother.csv
|
||||
```
|
||||
|
||||
This will read the disk in drive 0 and write out an information CSV file. It'll
|
||||
@@ -478,7 +499,7 @@ settings, I can rerun the decode without having to physically touch the disk
|
||||
like this:
|
||||
|
||||
```
|
||||
$ fluxengine read brother -s brother.flux -o brother.img --decoder.write_csv_to=brother.csv
|
||||
$ fluxengine read -c brother -s brother.flux -o brother.img --decoder.write_csv_to=brother.csv
|
||||
```
|
||||
|
||||
Apart from being drastically faster, this avoids touching the (potentially
|
||||
|
||||
@@ -2,9 +2,14 @@ syntax = "proto2";
|
||||
|
||||
import "google/protobuf/descriptor.proto";
|
||||
|
||||
extend google.protobuf.FieldOptions {
|
||||
optional string help = 50000;
|
||||
optional bool recurse = 50001 [default = true];
|
||||
extend google.protobuf.FieldOptions
|
||||
{
|
||||
optional string help = 50000;
|
||||
}
|
||||
|
||||
extend google.protobuf.MessageOptions
|
||||
{
|
||||
optional bool recurse = 50001 [default = true];
|
||||
}
|
||||
|
||||
enum IndexMode {
|
||||
@@ -29,21 +34,18 @@ enum FluxSourceSinkType {
|
||||
FLUXTYPE_DMK = 12;
|
||||
}
|
||||
|
||||
enum ImageReaderWriterType {
|
||||
IMAGETYPE_NOT_SET = 0;
|
||||
IMAGETYPE_D64 = 1;
|
||||
IMAGETYPE_D88 = 2;
|
||||
IMAGETYPE_DIM = 3;
|
||||
IMAGETYPE_DISKCOPY = 4;
|
||||
IMAGETYPE_FDI = 5;
|
||||
IMAGETYPE_IMD = 6;
|
||||
IMAGETYPE_IMG = 7;
|
||||
IMAGETYPE_JV3 = 8;
|
||||
IMAGETYPE_LDBS = 9;
|
||||
IMAGETYPE_NFD = 10;
|
||||
IMAGETYPE_NSI = 11;
|
||||
IMAGETYPE_RAW = 12;
|
||||
IMAGETYPE_TD0 = 13;
|
||||
enum ImageReaderWriterType
|
||||
{
|
||||
IMAGETYPE_NOT_SET = 0; IMAGETYPE_D64 = 1; IMAGETYPE_D88 = 2;
|
||||
IMAGETYPE_DIM = 3;
|
||||
IMAGETYPE_DISKCOPY = 4;
|
||||
IMAGETYPE_FDI = 5;
|
||||
IMAGETYPE_IMD = 6;
|
||||
IMAGETYPE_IMG = 7;
|
||||
IMAGETYPE_JV3 = 8;
|
||||
IMAGETYPE_LDBS = 9;
|
||||
IMAGETYPE_NFD = 10;
|
||||
IMAGETYPE_NSI = 11;
|
||||
IMAGETYPE_RAW = 12;
|
||||
IMAGETYPE_TD0 = 13;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "lib/core/utils.h"
|
||||
#include <fstream>
|
||||
#include <google/protobuf/text_format.h>
|
||||
#include <fmt/ranges.h>
|
||||
|
||||
static Config config;
|
||||
|
||||
@@ -181,35 +182,8 @@ ConfigProto* Config::combined()
|
||||
{
|
||||
_combinedConfig = _baseConfig;
|
||||
|
||||
/* First apply any standalone options. */
|
||||
|
||||
std::set<std::string> options = _appliedOptions;
|
||||
for (const auto& option : _baseConfig.option())
|
||||
{
|
||||
if (options.find(option.name()) != options.end())
|
||||
{
|
||||
_combinedConfig.MergeFrom(option.config());
|
||||
options.erase(option.name());
|
||||
}
|
||||
}
|
||||
|
||||
/* Then apply any group options. */
|
||||
|
||||
for (auto& group : _baseConfig.option_group())
|
||||
{
|
||||
const OptionProto* selectedOption = &*group.option().begin();
|
||||
|
||||
for (auto& option : group.option())
|
||||
{
|
||||
if (options.find(option.name()) != options.end())
|
||||
{
|
||||
selectedOption = &option;
|
||||
options.erase(option.name());
|
||||
}
|
||||
}
|
||||
|
||||
_combinedConfig.MergeFrom(selectedOption->config());
|
||||
}
|
||||
for (const auto& optionInfo : _appliedOptions)
|
||||
_combinedConfig.MergeFrom(optionInfo.option->config());
|
||||
|
||||
/* Add in the user overrides. */
|
||||
|
||||
@@ -241,51 +215,27 @@ std::vector<std::string> Config::validate()
|
||||
{
|
||||
std::vector<std::string> results;
|
||||
|
||||
std::set<std::string> optionNames = _appliedOptions;
|
||||
std::set<const OptionProto*> appliedOptions;
|
||||
for (const auto& option : _baseConfig.option())
|
||||
{
|
||||
if (optionNames.find(option.name()) != optionNames.end())
|
||||
/* Ensure that only one item in each group is set. */
|
||||
|
||||
std::map<const OptionGroupProto*, const OptionProto*> optionsByGroup;
|
||||
for (auto [group, option, hasArgument] : _appliedOptions)
|
||||
if (group)
|
||||
{
|
||||
appliedOptions.insert(&option);
|
||||
optionNames.erase(option.name());
|
||||
auto& o = optionsByGroup[group];
|
||||
if (o)
|
||||
results.push_back(
|
||||
fmt::format("multiple mutually exclusive values set for "
|
||||
"group '{}': valid values are: {}",
|
||||
group->comment(),
|
||||
fmt::join(std::views::transform(
|
||||
group->option(), &OptionProto::name),
|
||||
", ")));
|
||||
o = option;
|
||||
}
|
||||
}
|
||||
|
||||
/* Then apply any group options. */
|
||||
|
||||
for (auto& group : _baseConfig.option_group())
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for (auto& option : group.option())
|
||||
{
|
||||
if (optionNames.find(option.name()) != optionNames.end())
|
||||
{
|
||||
optionNames.erase(option.name());
|
||||
appliedOptions.insert(&option);
|
||||
|
||||
count++;
|
||||
if (count == 2)
|
||||
results.push_back(
|
||||
fmt::format("multiple mutually exclusive options set "
|
||||
"for group '{}'",
|
||||
group.comment()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for unknown options. */
|
||||
|
||||
if (!optionNames.empty())
|
||||
{
|
||||
for (auto& name : optionNames)
|
||||
results.push_back(fmt::format("'{}' is not a known option", name));
|
||||
}
|
||||
|
||||
/* Check option requirements. */
|
||||
|
||||
for (auto& option : appliedOptions)
|
||||
for (auto [group, option, hasArgument] : _appliedOptions)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -360,11 +310,12 @@ void Config::readBaseConfig(std::string data)
|
||||
error("couldn't load external config proto");
|
||||
}
|
||||
|
||||
const OptionProto& Config::findOption(const std::string& optionName)
|
||||
Config::OptionInfo Config::findOption(
|
||||
const std::string& name, const std::string value)
|
||||
{
|
||||
const OptionProto* found = nullptr;
|
||||
|
||||
auto searchOptionList = [&](auto& optionList)
|
||||
auto searchOptionList = [&](auto& optionList, const std::string& optionName)
|
||||
{
|
||||
for (const auto& option : optionList)
|
||||
{
|
||||
@@ -377,17 +328,39 @@ const OptionProto& Config::findOption(const std::string& optionName)
|
||||
return false;
|
||||
};
|
||||
|
||||
if (searchOptionList(base()->option()))
|
||||
return *found;
|
||||
/* First look for any group names which match. */
|
||||
|
||||
if (!value.empty())
|
||||
for (const auto& optionGroup : base()->option_group())
|
||||
if (optionGroup.name() == name)
|
||||
{
|
||||
/* The option must therefore be one of these. */
|
||||
|
||||
if (searchOptionList(optionGroup.option(), value))
|
||||
return {&optionGroup, found, true};
|
||||
|
||||
throw OptionNotFoundException(fmt::format(
|
||||
"value {} is not valid for option {}; valid values are: {}",
|
||||
value,
|
||||
name,
|
||||
fmt::join(std::views::transform(
|
||||
optionGroup.option(), &OptionProto::name),
|
||||
", ")));
|
||||
}
|
||||
|
||||
/* Now search for individual options. */
|
||||
|
||||
if (searchOptionList(base()->option(), name))
|
||||
return {nullptr, found, false};
|
||||
|
||||
for (const auto& optionGroup : base()->option_group())
|
||||
{
|
||||
if (searchOptionList(optionGroup.option()))
|
||||
return *found;
|
||||
if (optionGroup.name().empty())
|
||||
if (searchOptionList(optionGroup.option(), name))
|
||||
return {nullptr, found, false};
|
||||
}
|
||||
|
||||
throw OptionNotFoundException(
|
||||
fmt::format("option {} not found", optionName));
|
||||
throw OptionNotFoundException(fmt::format("option {} not found", name));
|
||||
}
|
||||
|
||||
void Config::checkOptionValid(const OptionProto& option)
|
||||
@@ -445,22 +418,20 @@ bool Config::isOptionValid(const OptionProto& option)
|
||||
}
|
||||
}
|
||||
|
||||
bool Config::isOptionValid(std::string option)
|
||||
{
|
||||
return isOptionValid(findOption(option));
|
||||
}
|
||||
|
||||
void Config::applyOption(const OptionProto& option)
|
||||
void Config::applyOption(const OptionInfo& optionInfo)
|
||||
{
|
||||
auto* option = optionInfo.option;
|
||||
log(OptionLogMessage{
|
||||
option.has_message() ? option.message() : option.comment()});
|
||||
option->has_message() ? option->message() : option->comment()});
|
||||
|
||||
_appliedOptions.insert(option.name());
|
||||
_appliedOptions.insert(optionInfo);
|
||||
}
|
||||
|
||||
void Config::applyOption(std::string option)
|
||||
bool Config::applyOption(const std::string& name, const std::string value)
|
||||
{
|
||||
applyOption(findOption(option));
|
||||
auto optionInfo = findOption(name, value);
|
||||
applyOption(optionInfo);
|
||||
return optionInfo.usesValue;
|
||||
}
|
||||
|
||||
void Config::clearOptions()
|
||||
|
||||
@@ -66,6 +66,18 @@ struct FluxConstructor
|
||||
|
||||
class Config
|
||||
{
|
||||
private:
|
||||
struct OptionInfo
|
||||
{
|
||||
bool operator==(const OptionInfo& other) const = default;
|
||||
std::strong_ordering operator<=>(
|
||||
const OptionInfo& other) const = default;
|
||||
|
||||
const OptionGroupProto* group;
|
||||
const OptionProto* option;
|
||||
bool usesValue;
|
||||
};
|
||||
|
||||
public:
|
||||
/* Direct access to the various proto layers. */
|
||||
|
||||
@@ -124,12 +136,12 @@ public:
|
||||
/* Option management: look up an option by name, determine whether an option
|
||||
* is valid, and apply an option. */
|
||||
|
||||
const OptionProto& findOption(const std::string& option);
|
||||
OptionInfo findOption(
|
||||
const std::string& name, const std::string value = "");
|
||||
void checkOptionValid(const OptionProto& option);
|
||||
bool isOptionValid(const OptionProto& option);
|
||||
bool isOptionValid(std::string option);
|
||||
void applyOption(const OptionProto& option);
|
||||
void applyOption(std::string option);
|
||||
void applyOption(const OptionInfo& optionInfo);
|
||||
bool applyOption(const std::string& name, const std::string value = "");
|
||||
void clearOptions();
|
||||
|
||||
/* Adjust overall inputs and outputs. */
|
||||
@@ -165,7 +177,7 @@ private:
|
||||
ConfigProto _baseConfig;
|
||||
ConfigProto _overridesConfig;
|
||||
ConfigProto _combinedConfig;
|
||||
std::set<std::string> _appliedOptions;
|
||||
std::set<OptionInfo> _appliedOptions;
|
||||
bool _configValid;
|
||||
|
||||
FluxSourceProto _verificationFluxSourceProto;
|
||||
|
||||
@@ -14,20 +14,20 @@ import "lib/config/layout.proto";
|
||||
|
||||
enum SupportStatus
|
||||
{
|
||||
UNSUPPORTED = 0;
|
||||
DINOSAUR = 1;
|
||||
UNICORN = 2;
|
||||
UNSUPPORTED = 0; DINOSAUR = 1; UNICORN = 2;
|
||||
}
|
||||
|
||||
// NEXT_TAG: 27
|
||||
message ConfigProto
|
||||
{
|
||||
option(recurse) = false;
|
||||
|
||||
optional string shortname = 1;
|
||||
optional string comment = 2;
|
||||
optional bool is_extension = 3;
|
||||
repeated string documentation = 4;
|
||||
optional SupportStatus read_support_status = 5 [ default = UNSUPPORTED ];
|
||||
optional SupportStatus write_support_status = 6 [ default = UNSUPPORTED ];
|
||||
optional SupportStatus read_support_status = 5 [default = UNSUPPORTED];
|
||||
optional SupportStatus write_support_status = 6 [default = UNSUPPORTED];
|
||||
|
||||
optional LayoutProto layout = 7;
|
||||
|
||||
@@ -51,28 +51,28 @@ message ConfigProto
|
||||
|
||||
message OptionPrerequisiteProto
|
||||
{
|
||||
optional string key = 1 [ (help) = "path to config value" ];
|
||||
repeated string value = 2 [ (help) = "list of required values" ];
|
||||
optional string key = 1 [(help) = "path to config value"];
|
||||
repeated string value = 2 [(help) = "list of required values"];
|
||||
}
|
||||
|
||||
// NEXT_TAG: 8
|
||||
message OptionProto
|
||||
{
|
||||
optional string name = 1 [ (help) = "option name" ];
|
||||
optional string comment = 2 [ (help) = "help text for option" ];
|
||||
optional string message = 3
|
||||
[ (help) = "message to display when option is in use" ];
|
||||
optional bool set_by_default = 6
|
||||
[ (help) = "this option is applied by default", default = false ];
|
||||
repeated OptionPrerequisiteProto prerequisite = 7
|
||||
[ (help) = "prerequisites for this option" ];
|
||||
optional string name = 1 [(help) = "option name"];
|
||||
optional string comment = 2 [(help) = "help text for option"];
|
||||
optional string message =
|
||||
3 [(help) = "message to display when option is in use"];
|
||||
optional bool set_by_default =
|
||||
6 [(help) = "this option is applied by default", default = false];
|
||||
repeated OptionPrerequisiteProto prerequisite =
|
||||
7 [(help) = "prerequisites for this option"];
|
||||
|
||||
optional ConfigProto config = 4
|
||||
[ (help) = "option data", (recurse) = false ];
|
||||
optional ConfigProto config = 4 [(help) = "option data"];
|
||||
}
|
||||
|
||||
message OptionGroupProto
|
||||
{
|
||||
optional string comment = 1 [ (help) = "help text for option group" ];
|
||||
repeated OptionProto option = 2;
|
||||
optional string comment = 1 [(help) = "help text for option group"];
|
||||
optional string name = 2 [(help) = "option group name"];
|
||||
repeated OptionProto option = 3;
|
||||
}
|
||||
|
||||
@@ -13,17 +13,23 @@ static std::vector<Flag*> all_flags;
|
||||
static std::map<const std::string, Flag*> flags_by_name;
|
||||
|
||||
static void doHelp();
|
||||
static void doLoadConfig(const std::string& filename);
|
||||
static void doShowConfig();
|
||||
static void doDoc();
|
||||
|
||||
static FlagGroup helpGroup;
|
||||
static ActionFlag helpFlag = ActionFlag({"--help"}, "Shows the help.", doHelp);
|
||||
|
||||
static ActionFlag showConfigFlag = ActionFlag({"--config", "-C"},
|
||||
static FlagGroup configGroup;
|
||||
static ActionFlag loadConfigFlag({"--config", "-c"},
|
||||
"Reads an internal or external configuration file.",
|
||||
doLoadConfig);
|
||||
|
||||
static ActionFlag showConfigFlag({"--show-config", "-C"},
|
||||
"Shows the currently set configuration and halts.",
|
||||
doShowConfig);
|
||||
|
||||
static ActionFlag docFlag = ActionFlag(
|
||||
static ActionFlag docFlag(
|
||||
{"--doc"}, "Shows the available configuration options and halts.", doDoc);
|
||||
|
||||
FlagGroup::FlagGroup()
|
||||
@@ -152,7 +158,7 @@ std::vector<std::string> FlagGroup::parseFlagsWithFilenames(int argc,
|
||||
index += usesthat;
|
||||
}
|
||||
else
|
||||
globalConfig().applyOption(path);
|
||||
usesthat = globalConfig().applyOption(path, value);
|
||||
}
|
||||
else
|
||||
error("unrecognised flag '-{}'; try --help", key);
|
||||
@@ -182,17 +188,17 @@ void FlagGroup::parseFlags(int argc,
|
||||
"non-option parameter '{}' seen (try --help)", *filenames.begin());
|
||||
}
|
||||
|
||||
static void doLoadConfig(const std::string& filename)
|
||||
{
|
||||
globalConfig().readBaseConfigFile(filename);
|
||||
}
|
||||
|
||||
void FlagGroup::parseFlagsWithConfigFiles(int argc,
|
||||
const char* argv[],
|
||||
const std::map<std::string, const ConfigProto*>& configFiles)
|
||||
{
|
||||
parseFlags(argc,
|
||||
argv,
|
||||
[&](const auto& filename)
|
||||
{
|
||||
globalConfig().readBaseConfigFile(filename);
|
||||
return true;
|
||||
});
|
||||
globalConfig().readBaseConfigFile("_global_options");
|
||||
FlagGroup({this, &configGroup}).parseFlags(argc, argv);
|
||||
}
|
||||
|
||||
void FlagGroup::checkInitialised() const
|
||||
@@ -264,7 +270,8 @@ static void doShowConfig()
|
||||
|
||||
static void doDoc()
|
||||
{
|
||||
const auto fields = findAllProtoFields(globalConfig().base());
|
||||
const auto fields =
|
||||
findAllPossibleProtoFields(globalConfig().base()->GetDescriptor());
|
||||
for (const auto field : fields)
|
||||
{
|
||||
const std::string& path = field.first;
|
||||
|
||||
@@ -83,13 +83,23 @@ public:
|
||||
const std::string helptext,
|
||||
std::function<void(void)> callback):
|
||||
Flag(names, helptext),
|
||||
_callback(callback)
|
||||
_voidCallback(callback),
|
||||
_hasArgument(false)
|
||||
{
|
||||
}
|
||||
|
||||
ActionFlag(const std::vector<std::string>& names,
|
||||
const std::string helptext,
|
||||
std::function<void(const std::string&)> callback):
|
||||
Flag(names, helptext),
|
||||
_callback(callback),
|
||||
_hasArgument(true)
|
||||
{
|
||||
}
|
||||
|
||||
bool hasArgument() const override
|
||||
{
|
||||
return false;
|
||||
return _hasArgument;
|
||||
}
|
||||
|
||||
const std::string defaultValueAsString() const override
|
||||
@@ -99,11 +109,16 @@ public:
|
||||
|
||||
void set(const std::string& value) override
|
||||
{
|
||||
_callback();
|
||||
if (_hasArgument)
|
||||
_callback(value);
|
||||
else
|
||||
_voidCallback();
|
||||
}
|
||||
|
||||
private:
|
||||
const std::function<void(void)> _callback;
|
||||
const std::function<void(const std::string&)> _callback;
|
||||
const std::function<void(void)> _voidCallback;
|
||||
bool _hasArgument;
|
||||
};
|
||||
|
||||
class SettableFlag : public Flag
|
||||
|
||||
@@ -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
|
||||
{
|
||||
@@ -66,6 +67,23 @@ static uint64_t toUint64(const std::string& value)
|
||||
return d;
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
auto stem = dmatch[1];
|
||||
index = std::stoi(dmatch[2]);
|
||||
|
||||
item = stem;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static ProtoField resolveProtoPath(
|
||||
google::protobuf::Message* message, const std::string& path, bool create)
|
||||
{
|
||||
@@ -86,6 +104,10 @@ static ProtoField resolveProtoPath(
|
||||
std::stringstream ss(leading);
|
||||
while (std::getline(ss, item, '.'))
|
||||
{
|
||||
static const std::regex INDEX_REGEX("(\\w+)\\[([0-9]+)\\]");
|
||||
|
||||
int index = splitIndexedField(item);
|
||||
|
||||
const auto* field = descriptor->FindFieldByName(item);
|
||||
if (!field)
|
||||
throw ProtoPathNotFoundException(
|
||||
@@ -95,6 +117,14 @@ static ProtoField resolveProtoPath(
|
||||
"config field '{}' in '{}' is not a message", item, path));
|
||||
|
||||
const auto* reflection = message->GetReflection();
|
||||
if ((field->label() !=
|
||||
google::protobuf::FieldDescriptor::LABEL_REPEATED) &&
|
||||
(index != -1))
|
||||
throw ProtoPathNotFoundException(fmt::format(
|
||||
"config field '{}[{}]' is indexed, but not repeated",
|
||||
item,
|
||||
index));
|
||||
|
||||
switch (field->label())
|
||||
{
|
||||
case google::protobuf::FieldDescriptor::LABEL_OPTIONAL:
|
||||
@@ -106,16 +136,15 @@ static ProtoField resolveProtoPath(
|
||||
break;
|
||||
|
||||
case google::protobuf::FieldDescriptor::LABEL_REPEATED:
|
||||
if (reflection->FieldSize(*message, field) == 0)
|
||||
{
|
||||
if (create)
|
||||
message = reflection->AddMessage(message, field);
|
||||
else
|
||||
fail();
|
||||
}
|
||||
else
|
||||
message =
|
||||
reflection->MutableRepeatedMessage(message, field, 0);
|
||||
if (index == -1)
|
||||
throw ProtoPathNotFoundException(fmt::format(
|
||||
"config field '{}' is repeated and must be indexed",
|
||||
item));
|
||||
while (reflection->FieldSize(*message, field) <= index)
|
||||
reflection->AddMessage(message, field);
|
||||
|
||||
message =
|
||||
reflection->MutableRepeatedMessage(message, field, index);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -125,11 +154,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 ProtoField(path, message, field, index);
|
||||
}
|
||||
|
||||
ProtoField makeProtoPath(
|
||||
@@ -144,130 +174,324 @@ ProtoField findProtoPath(
|
||||
return resolveProtoPath(message, path, /* create= */ false);
|
||||
}
|
||||
|
||||
void setProtoFieldFromString(ProtoField& protoField, const std::string& value)
|
||||
static bool parseBoolean(const std::string& value)
|
||||
{
|
||||
google::protobuf::Message* message = protoField.first;
|
||||
const google::protobuf::FieldDescriptor* field = protoField.second;
|
||||
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* reflection = message->GetReflection();
|
||||
switch (field->type())
|
||||
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->number();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void updateRepeatedField(
|
||||
google::protobuf::MutableRepeatedFieldRef<T> mrfr, int index, T value)
|
||||
{
|
||||
mrfr.Set(index, value);
|
||||
}
|
||||
|
||||
void ProtoField::set(const std::string& value)
|
||||
{
|
||||
const auto* reflection = _message->GetReflection();
|
||||
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 },
|
||||
};
|
||||
|
||||
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_ENUM:
|
||||
{
|
||||
const auto* enumfield = field->enum_type();
|
||||
const auto* enumvalue = enumfield->FindValueByName(value);
|
||||
if (!enumvalue)
|
||||
error("unrecognised enum value '{}'", value);
|
||||
|
||||
reflection->SetEnum(message, field, enumvalue);
|
||||
break;
|
||||
}
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
|
||||
if (field->containing_oneof() && value.empty())
|
||||
{
|
||||
reflection->MutableMessage(message, field);
|
||||
case google::protobuf::FieldDescriptor::TYPE_FLOAT:
|
||||
updateRepeatedField(
|
||||
reflection->GetMutableRepeatedFieldRef<float>(
|
||||
_message, _field),
|
||||
_index,
|
||||
toFloat(value));
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
default:
|
||||
error("can't set this config value type");
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
|
||||
updateRepeatedField(
|
||||
reflection->GetMutableRepeatedFieldRef<double>(
|
||||
_message, _field),
|
||||
_index,
|
||||
toDouble(value));
|
||||
break;
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_INT32:
|
||||
updateRepeatedField(
|
||||
reflection->GetMutableRepeatedFieldRef<int32_t>(
|
||||
_message, _field),
|
||||
_index,
|
||||
(int32_t)toInt64(value));
|
||||
break;
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_INT64:
|
||||
updateRepeatedField(
|
||||
reflection->GetMutableRepeatedFieldRef<int64_t>(
|
||||
_message, _field),
|
||||
_index,
|
||||
toInt64(value));
|
||||
break;
|
||||
|
||||
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;
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_BOOL:
|
||||
updateRepeatedField(
|
||||
reflection->GetMutableRepeatedFieldRef<bool>(
|
||||
_message, _field),
|
||||
_index,
|
||||
parseBoolean(value));
|
||||
break;
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_ENUM:
|
||||
updateRepeatedField(
|
||||
reflection->GetMutableRepeatedFieldRef<int32_t>(
|
||||
_message, _field),
|
||||
_index,
|
||||
parseEnum(_field, value));
|
||||
break;
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
|
||||
error("'{}' is a message and can't be directly set",
|
||||
_field->name());
|
||||
|
||||
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->SetEnumValue(
|
||||
_message, _field, parseEnum(_field, value));
|
||||
break;
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
|
||||
error("'{}[{}]' is a message and can't be directly set",
|
||||
_field->name(),
|
||||
_index);
|
||||
|
||||
default:
|
||||
error("can't set this config value type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string getProtoFieldValue(ProtoField& protoField)
|
||||
std::string ProtoField::get() const
|
||||
{
|
||||
google::protobuf::Message* message = protoField.first;
|
||||
const google::protobuf::FieldDescriptor* field = protoField.second;
|
||||
|
||||
const auto* reflection = message->GetReflection();
|
||||
switch (field->type())
|
||||
const auto* reflection = _message->GetReflection();
|
||||
if (_field->label() == google::protobuf::FieldDescriptor::LABEL_REPEATED)
|
||||
{
|
||||
case google::protobuf::FieldDescriptor::TYPE_FLOAT:
|
||||
return fmt::format("{}", reflection->GetFloat(*message, field));
|
||||
if (_index == -1)
|
||||
error("field '{}' is repeated but no index is provided",
|
||||
_field->name());
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
|
||||
return fmt::format("{}", reflection->GetDouble(*message, field));
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_INT32:
|
||||
return std::to_string(reflection->GetInt32(*message, field));
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_INT64:
|
||||
return std::to_string(reflection->GetInt64(*message, field));
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_UINT32:
|
||||
return std::to_string(reflection->GetUInt32(*message, field));
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_UINT64:
|
||||
return std::to_string(reflection->GetUInt64(*message, field));
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_STRING:
|
||||
return reflection->GetString(*message, field);
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_BOOL:
|
||||
return std::to_string(reflection->GetBool(*message, field));
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_ENUM:
|
||||
switch (_field->type())
|
||||
{
|
||||
const auto* enumvalue = reflection->GetEnum(*message, field);
|
||||
return enumvalue->name();
|
||||
case google::protobuf::FieldDescriptor::TYPE_FLOAT:
|
||||
return fmt::format("{:g}",
|
||||
reflection->GetRepeatedFloat(*_message, _field, _index));
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
|
||||
return fmt::format("{:g}",
|
||||
reflection->GetRepeatedDouble(*_message, _field, _index));
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_INT32:
|
||||
return std::to_string(
|
||||
reflection->GetRepeatedInt32(*_message, _field, _index));
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_INT64:
|
||||
return std::to_string(
|
||||
reflection->GetRepeatedInt64(*_message, _field, _index));
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_UINT32:
|
||||
return std::to_string(
|
||||
reflection->GetRepeatedUInt32(*_message, _field, _index));
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_UINT64:
|
||||
return std::to_string(
|
||||
reflection->GetRepeatedUInt64(*_message, _field, _index));
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_STRING:
|
||||
return reflection->GetRepeatedString(*_message, _field, _index);
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
|
||||
error("'{}' is a message and can't be directly fetched",
|
||||
_field->name());
|
||||
|
||||
default:
|
||||
error("unknown field type when fetching repeated field '{}'",
|
||||
_field->name());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_index != -1)
|
||||
error("field '{}' is not repeated but an index is provided",
|
||||
_field->name());
|
||||
switch (_field->type())
|
||||
{
|
||||
case google::protobuf::FieldDescriptor::TYPE_FLOAT:
|
||||
return fmt::format(
|
||||
"{:g}", reflection->GetFloat(*_message, _field));
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
|
||||
error("cannot fetch message value");
|
||||
case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
|
||||
return fmt::format(
|
||||
"{:g}", reflection->GetDouble(*_message, _field));
|
||||
|
||||
default:
|
||||
error("unknown field type when fetching");
|
||||
case google::protobuf::FieldDescriptor::TYPE_INT32:
|
||||
return std::to_string(reflection->GetInt32(*_message, _field));
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_INT64:
|
||||
return std::to_string(reflection->GetInt64(*_message, _field));
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_UINT32:
|
||||
return std::to_string(reflection->GetUInt32(*_message, _field));
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_UINT64:
|
||||
return std::to_string(reflection->GetUInt64(*_message, _field));
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_STRING:
|
||||
return reflection->GetString(*_message, _field);
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_BOOL:
|
||||
return std::to_string(reflection->GetBool(*_message, _field));
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_ENUM:
|
||||
{
|
||||
const auto* enumvalue = reflection->GetEnum(*_message, _field);
|
||||
return (std::string)enumvalue->name();
|
||||
}
|
||||
|
||||
case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
|
||||
error("'{}[{}]' is a message and can't be directly set",
|
||||
_field->name(),
|
||||
_index);
|
||||
|
||||
default:
|
||||
error("unknown field type when fetching '{}'", _field->name());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
google::protobuf::Message* ProtoField::getMessage() const
|
||||
{
|
||||
const auto* reflection = _message->GetReflection();
|
||||
if (_field->label() == google::protobuf::FieldDescriptor::LABEL_REPEATED)
|
||||
{
|
||||
if (_index == -1)
|
||||
error("field '{}' is repeated but no index is provided",
|
||||
_field->name());
|
||||
|
||||
return reflection->MutableRepeatedMessage(_message, _field, _index);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_index != -1)
|
||||
error("field '{}' is not repeated but an index is provided",
|
||||
_field->name());
|
||||
|
||||
return reflection->MutableMessage(_message, _field);
|
||||
}
|
||||
}
|
||||
|
||||
std::string ProtoField::getBytes() const
|
||||
{
|
||||
const auto* reflection = _message->GetReflection();
|
||||
if (_field->label() == google::protobuf::FieldDescriptor::LABEL_REPEATED)
|
||||
{
|
||||
if (_index == -1)
|
||||
error("field '{}' is repeated but no index is provided",
|
||||
_field->name());
|
||||
|
||||
return reflection->GetRepeatedString(*_message, _field, _index);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_index != -1)
|
||||
error("field '{}' is not repeated but an index is provided",
|
||||
_field->name());
|
||||
|
||||
return reflection->GetString(*_message, _field);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,15 +499,13 @@ void setProtoByString(google::protobuf::Message* message,
|
||||
const std::string& path,
|
||||
const std::string& value)
|
||||
{
|
||||
ProtoField protoField = makeProtoPath(message, path);
|
||||
setProtoFieldFromString(protoField, value);
|
||||
makeProtoPath(message, path).set(value);
|
||||
}
|
||||
|
||||
std::string getProtoByString(
|
||||
google::protobuf::Message* message, const std::string& path)
|
||||
{
|
||||
ProtoField protoField = findProtoPath(message, path);
|
||||
return getProtoFieldValue(protoField);
|
||||
return findProtoPath(message, path).get();
|
||||
}
|
||||
|
||||
std::set<unsigned> iterate(unsigned start, unsigned count)
|
||||
@@ -294,11 +516,17 @@ std::set<unsigned> iterate(unsigned start, unsigned count)
|
||||
return set;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
std::map<std::string, const google::protobuf::FieldDescriptor*>
|
||||
findAllProtoFields(google::protobuf::Message* message)
|
||||
findAllPossibleProtoFields(const google::protobuf::Descriptor* descriptor)
|
||||
{
|
||||
std::map<std::string, const google::protobuf::FieldDescriptor*> fields;
|
||||
const auto* descriptor = message->GetDescriptor();
|
||||
|
||||
std::function<void(const google::protobuf::Descriptor*, const std::string&)>
|
||||
recurse = [&](auto* d, const auto& s)
|
||||
@@ -306,10 +534,12 @@ findAllProtoFields(google::protobuf::Message* message)
|
||||
for (int i = 0; i < d->field_count(); i++)
|
||||
{
|
||||
const google::protobuf::FieldDescriptor* f = d->field(i);
|
||||
std::string n = s + f->name();
|
||||
std::string n = s + (std::string)f->name();
|
||||
|
||||
if (f->options().GetExtension(::recurse) &&
|
||||
(f->type() == google::protobuf::FieldDescriptor::TYPE_MESSAGE))
|
||||
if (f->label() == google::protobuf::FieldDescriptor::LABEL_REPEATED)
|
||||
n += "[]";
|
||||
|
||||
if (shouldRecurse(f))
|
||||
recurse(f->message_type(), n + ".");
|
||||
|
||||
fields[n] = f;
|
||||
@@ -320,10 +550,47 @@ findAllProtoFields(google::protobuf::Message* message)
|
||||
return fields;
|
||||
}
|
||||
|
||||
ConfigProto parseConfigBytes(const std::string_view& data)
|
||||
std::vector<ProtoField> findAllProtoFields(google::protobuf::Message* message)
|
||||
{
|
||||
ConfigProto proto;
|
||||
if (!proto.ParseFromArray(data.begin(), data.size()))
|
||||
error("invalid internal config data");
|
||||
return proto;
|
||||
std::vector<ProtoField> allFields;
|
||||
|
||||
std::function<void(google::protobuf::Message*, const std::string&)>
|
||||
recurse = [&](auto* message, const auto& name)
|
||||
{
|
||||
const auto* reflection = message->GetReflection();
|
||||
std::vector<const google::protobuf::FieldDescriptor*> fields;
|
||||
reflection->ListFields(*message, &fields);
|
||||
|
||||
for (const auto* f : fields)
|
||||
{
|
||||
auto basename = name;
|
||||
if (!basename.empty())
|
||||
basename += '.';
|
||||
basename += f->name();
|
||||
|
||||
if (f->label() == google::protobuf::FieldDescriptor::LABEL_REPEATED)
|
||||
{
|
||||
for (int i = 0; i < reflection->FieldSize(*message, f); i++)
|
||||
{
|
||||
const auto n = fmt::format("{}[{}]", basename, i);
|
||||
if (shouldRecurse(f))
|
||||
recurse(
|
||||
reflection->MutableRepeatedMessage(message, f, i),
|
||||
n);
|
||||
else
|
||||
allFields.push_back(ProtoField(n, message, f, i));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (shouldRecurse(f))
|
||||
recurse(reflection->MutableMessage(message, f), basename);
|
||||
else
|
||||
allFields.push_back(ProtoField(basename, message, f));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
recurse(message, "");
|
||||
return allFields;
|
||||
}
|
||||
|
||||
@@ -14,9 +14,44 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::pair<google::protobuf::Message*,
|
||||
const google::protobuf::FieldDescriptor*>
|
||||
ProtoField;
|
||||
class ProtoField
|
||||
{
|
||||
public:
|
||||
ProtoField(const std::string& path,
|
||||
google::protobuf::Message* message,
|
||||
const google::protobuf::FieldDescriptor* field,
|
||||
int index = -1):
|
||||
_path(path),
|
||||
_message(message),
|
||||
_field(field),
|
||||
_index(index)
|
||||
{
|
||||
}
|
||||
|
||||
void set(const std::string& value);
|
||||
std::string get() const;
|
||||
google::protobuf::Message* getMessage() const;
|
||||
std::string getBytes() const;
|
||||
|
||||
bool operator==(const ProtoField& other) const = default;
|
||||
std::strong_ordering operator<=>(const ProtoField& other) const = default;
|
||||
|
||||
const std::string& path() const
|
||||
{
|
||||
return _path;
|
||||
}
|
||||
|
||||
const google::protobuf::FieldDescriptor* descriptor() const
|
||||
{
|
||||
return _field;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _path;
|
||||
google::protobuf::Message* _message;
|
||||
const google::protobuf::FieldDescriptor* _field;
|
||||
int _index;
|
||||
};
|
||||
|
||||
extern ProtoField makeProtoPath(
|
||||
google::protobuf::Message* message, const std::string& path);
|
||||
@@ -34,9 +69,19 @@ extern std::string getProtoByString(
|
||||
extern std::set<unsigned> iterate(unsigned start, unsigned count);
|
||||
|
||||
extern std::map<std::string, const google::protobuf::FieldDescriptor*>
|
||||
findAllProtoFields(google::protobuf::Message* message);
|
||||
findAllPossibleProtoFields(const google::protobuf::Descriptor* descriptor);
|
||||
|
||||
extern ConfigProto parseConfigBytes(const std::string_view& bytes);
|
||||
extern std::vector<ProtoField> findAllProtoFields(
|
||||
google::protobuf::Message* message);
|
||||
|
||||
template <class T>
|
||||
static inline const T parseProtoBytes(const std::string_view& bytes)
|
||||
{
|
||||
T proto;
|
||||
if (!proto.ParseFromArray(bytes.begin(), bytes.size()))
|
||||
error("invalid internal proto data");
|
||||
return proto;
|
||||
}
|
||||
|
||||
extern const std::map<std::string, const ConfigProto*> formats;
|
||||
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
#define mkdir(A, B) _mkdir(A)
|
||||
#endif
|
||||
|
||||
#define STRINGIFY(a) XSTRINGIFY(a)
|
||||
#define XSTRINGIFY(a) #a
|
||||
|
||||
template <class T>
|
||||
static inline std::vector<T> vector_of(T item)
|
||||
{
|
||||
|
||||
@@ -29,7 +29,8 @@ static unsigned getTrackStep()
|
||||
{
|
||||
case DRIVETYPE_40TRACK:
|
||||
error(
|
||||
"you can't read/write an 80 track image from/to a 40 track "
|
||||
"you can't read/write an 80 track image from/to a 40 "
|
||||
"track "
|
||||
"drive");
|
||||
|
||||
case DRIVETYPE_80TRACK:
|
||||
@@ -37,7 +38,8 @@ static unsigned getTrackStep()
|
||||
|
||||
case DRIVETYPE_APPLE2:
|
||||
error(
|
||||
"you can't read/write an 80 track image from/to an Apple II "
|
||||
"you can't read/write an 80 track image from/to an "
|
||||
"Apple II "
|
||||
"drive");
|
||||
}
|
||||
}
|
||||
|
||||
9
lib/external/fl2.proto
vendored
9
lib/external/fl2.proto
vendored
@@ -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 {
|
||||
|
||||
@@ -5,13 +5,14 @@ encoders = {}
|
||||
|
||||
|
||||
@Rule
|
||||
def protoencode_single(self, name, srcs: Targets, proto, symbol):
|
||||
def protoencode_single(self, name, srcs: Targets, proto, include, symbol):
|
||||
if proto not in encoders:
|
||||
r = cxxprogram(
|
||||
name="protoencode_" + proto,
|
||||
srcs=["scripts/protoencode.cc"],
|
||||
cflags=["-DPROTO=" + proto],
|
||||
cflags=["-DPROTO=" + proto, "-DINCLUDE="+include],
|
||||
deps=[
|
||||
"lib/core",
|
||||
"lib/config+proto_lib",
|
||||
"lib/fluxsource+proto_lib",
|
||||
"lib/fluxsink+proto_lib",
|
||||
@@ -41,12 +42,13 @@ def protoencode_single(self, name, srcs: Targets, proto, symbol):
|
||||
|
||||
|
||||
@Rule
|
||||
def protoencode(self, name, proto, srcs: TargetsMap, symbol):
|
||||
def protoencode(self, name, proto, include,srcs: TargetsMap, symbol):
|
||||
encoded = [
|
||||
protoencode_single(
|
||||
name=f"{k}_cc",
|
||||
srcs=[v],
|
||||
proto=proto,
|
||||
include=include,
|
||||
symbol=f"{symbol}_{k}_pb",
|
||||
)
|
||||
for k, v in srcs.items()
|
||||
|
||||
@@ -14,10 +14,10 @@ destfile=$dir/dest.img
|
||||
|
||||
dd if=/dev/urandom of=$srcfile bs=1048576 count=2 2>&1
|
||||
|
||||
echo $fluxengine write $format -i $srcfile -d $fluxfile --drive.rotational_period_ms=200 $flags
|
||||
$fluxengine write $format -i $srcfile -d $fluxfile --drive.rotational_period_ms=200 $flags
|
||||
echo $fluxengine read $format -s $fluxfile -o $destfile --drive.rotational_period_ms=200 $flags
|
||||
$fluxengine read $format -s $fluxfile -o $destfile --drive.rotational_period_ms=200 $flags
|
||||
echo $fluxengine write -c $format -i $srcfile -d $fluxfile --drive.rotational_period_ms=200 $flags
|
||||
$fluxengine write -c $format -i $srcfile -d $fluxfile --drive.rotational_period_ms=200 $flags
|
||||
echo $fluxengine read -c $format -s $fluxfile -o $destfile --drive.rotational_period_ms=200 $flags
|
||||
$fluxengine read -c $format -s $fluxfile -o $destfile --drive.rotational_period_ms=200 $flags
|
||||
if [ ! -s $destfile ]; then
|
||||
echo "Zero length output file!" >&2
|
||||
exit 1
|
||||
|
||||
@@ -28,7 +28,7 @@ static void addExample(std::vector<std::string>& examples,
|
||||
else
|
||||
return;
|
||||
|
||||
r += fmt::format(" {}", name);
|
||||
r += fmt::format(" -c {}", name);
|
||||
if (format)
|
||||
r += fmt::format(" --{}", format->name());
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ static std::string supportStatus(SupportStatus status)
|
||||
return "";
|
||||
}
|
||||
|
||||
return "";
|
||||
return "";
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
@@ -43,7 +43,9 @@ int main(int argc, const char* argv[])
|
||||
{
|
||||
const auto* descriptor =
|
||||
FilesystemProto::FilesystemType_descriptor();
|
||||
auto name = descriptor->FindValueByNumber(fs.type())->name();
|
||||
auto name =
|
||||
(std::string)descriptor->FindValueByNumber(fs.type())
|
||||
->name();
|
||||
|
||||
filesystems.insert(name);
|
||||
}
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
#include <google/protobuf/io/zero_copy_stream_impl.h>
|
||||
#include <fstream>
|
||||
#include "fmt/format.h"
|
||||
#include "lib/core/globals.h"
|
||||
#include "tests/testproto.pb.h"
|
||||
#include "lib/config/config.pb.h"
|
||||
#include <sstream>
|
||||
#include <locale>
|
||||
|
||||
#define STRINGIFY(s) #s
|
||||
const std::string protoname = STRINGIFY(PROTO);
|
||||
|
||||
static uint32_t readu8(std::string::iterator& it, std::string::iterator end)
|
||||
{
|
||||
@@ -125,6 +126,7 @@ int main(int argc, const char* argv[])
|
||||
|
||||
output << "#include \"lib/core/globals.h\"\n"
|
||||
<< "#include \"lib/config/proto.h\"\n"
|
||||
<< "#include \"" STRINGIFY(INCLUDE) "\"\n"
|
||||
<< "#include <string_view>\n"
|
||||
<< "static const uint8_t " << name << "_rawData[] = {";
|
||||
|
||||
@@ -143,11 +145,11 @@ int main(int argc, const char* argv[])
|
||||
output << "\n};\n";
|
||||
output << "extern const std::string_view " << name << "_data;\n";
|
||||
output << "const std::string_view " << name
|
||||
<< "_data = std::string_view((const char*)" << name << "_rawData, " << data.size()
|
||||
<< ");\n";
|
||||
output << "extern const ConfigProto " << name << ";\n";
|
||||
output << "const ConfigProto " << name << " = parseConfigBytes("
|
||||
<< argv[3] << "_data);\n";
|
||||
<< "_data = std::string_view((const char*)" << name << "_rawData, "
|
||||
<< data.size() << ");\n";
|
||||
output << "extern const " << protoname << " " << name << ";\n";
|
||||
output << "const " << protoname << " " << name << " = parseProtoBytes<"
|
||||
<< protoname << ">(" << argv[3] << "_data);\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -5,10 +5,15 @@ cxxprogram(
|
||||
srcs=[
|
||||
"./fluxengine.cc",
|
||||
"./fluxengine.h",
|
||||
"./fluxfile.cc",
|
||||
"./fluxfile.h",
|
||||
"./fe-analysedriveresponse.cc",
|
||||
"./fe-analyselayout.cc",
|
||||
"./fe-convert.cc",
|
||||
"./fe-format.cc",
|
||||
"./fe-fluxfilels.cc",
|
||||
"./fe-fluxfilerm.cc",
|
||||
"./fe-fluxfilecp.cc",
|
||||
"./fe-getdiskinfo.cc",
|
||||
"./fe-getfile.cc",
|
||||
"./fe-getfileinfo.cc",
|
||||
@@ -39,6 +44,7 @@ cxxprogram(
|
||||
"+z_lib",
|
||||
"dep/adflib",
|
||||
"dep/agg",
|
||||
"dep/alphanum",
|
||||
"dep/fatfs",
|
||||
"dep/hfsutils",
|
||||
"dep/libusbp",
|
||||
|
||||
@@ -22,9 +22,7 @@ static StringFlag destFlux({"--dest", "-d"},
|
||||
globalConfig().setFluxSink(value);
|
||||
});
|
||||
|
||||
static IntFlag destTrack({"--cylinder", "-c"}, "track to write to", 0);
|
||||
|
||||
static IntFlag destHead({"--head", "-h"}, "head to write to", 0);
|
||||
static StringFlag destTracks({"--tracks", "-t"}, "tracks to write to", "c0h0");
|
||||
|
||||
static DoubleFlag minInterval(
|
||||
{"--min-interval-us"}, "Minimum pulse interval", 2.0);
|
||||
@@ -251,11 +249,14 @@ int mainAnalyseDriveResponse(int argc, const char* argv[])
|
||||
|
||||
if (globalConfig()->flux_sink().type() != FLUXTYPE_DRIVE)
|
||||
error("this only makes sense with a real disk drive");
|
||||
auto tracks = parseCylinderHeadsString(destTracks);
|
||||
if (tracks.size() != 1)
|
||||
error("you must specify exactly one track");
|
||||
|
||||
usbSetDrive(globalConfig()->drive().drive(),
|
||||
globalConfig()->drive().high_density(),
|
||||
globalConfig()->drive().index_mode());
|
||||
usbSeek(destTrack);
|
||||
usbSeek(tracks[0].cylinder);
|
||||
|
||||
std::cout << "Measuring rotational speed...\n";
|
||||
nanoseconds_t period = usbGetRotationalPeriod(0);
|
||||
@@ -291,12 +292,12 @@ int mainAnalyseDriveResponse(int argc, const char* argv[])
|
||||
outFluxmap.appendPulse();
|
||||
}
|
||||
|
||||
usbWrite(destHead, outFluxmap.rawBytes(), 0);
|
||||
usbWrite(tracks[0].head, outFluxmap.rawBytes(), 0);
|
||||
|
||||
/* Read the test pattern in again. */
|
||||
|
||||
Fluxmap inFluxmap;
|
||||
inFluxmap.appendBytes(usbRead(destHead, true, period, 0));
|
||||
inFluxmap.appendBytes(usbRead(tracks[0].head, true, period, 0));
|
||||
|
||||
/* Compute histogram. */
|
||||
|
||||
|
||||
84
src/fe-fluxfilecp.cc
Normal file
84
src/fe-fluxfilecp.cc
Normal file
@@ -0,0 +1,84 @@
|
||||
#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/locations.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 StringFlag inputFilenameFlag({"-i", "--input"}, "input flux file");
|
||||
static StringFlag outputFilenameFlag(
|
||||
{"-o", "--output"}, "output flux file (must exist)");
|
||||
static StringFlag tracksFlag({"-t", "--tracks"}, "tracks to copy");
|
||||
|
||||
static const TrackFluxProto* findTrack(
|
||||
const FluxFileProto& f, int cylinder, int head)
|
||||
{
|
||||
for (const auto& trackFlux : f.track())
|
||||
if ((trackFlux.track() == cylinder) && (trackFlux.head() == head))
|
||||
return &trackFlux;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static TrackFluxProto* findOrMakeTrack(FluxFileProto& f, int cylinder, int head)
|
||||
{
|
||||
for (auto& trackFlux : *f.mutable_track())
|
||||
if ((trackFlux.track() == cylinder) && (trackFlux.head() == head))
|
||||
return &trackFlux;
|
||||
|
||||
TrackFluxProto* tf = f.add_track();
|
||||
tf->set_track(cylinder);
|
||||
tf->set_head(head);
|
||||
return tf;
|
||||
}
|
||||
|
||||
int mainFluxfileCp(int argc, const char* argv[])
|
||||
{
|
||||
flags.parseFlags(argc, argv);
|
||||
if (!inputFilenameFlag.isSet())
|
||||
error("you must specify an input filename with -i");
|
||||
if (!outputFilenameFlag.isSet())
|
||||
error("you must specify an output filename with -o");
|
||||
|
||||
fmt::print(
|
||||
"{} -> {}:\n", inputFilenameFlag.get(), outputFilenameFlag.get());
|
||||
FluxFileProto inf = loadFl2File(inputFilenameFlag.get());
|
||||
FluxFileProto outf = loadFl2File(outputFilenameFlag.get());
|
||||
|
||||
bool changed = false;
|
||||
for (const auto& location : parseCylinderHeadsString(tracksFlag))
|
||||
{
|
||||
const TrackFluxProto* intrack =
|
||||
findTrack(inf, location.cylinder, location.head);
|
||||
if (!intrack)
|
||||
{
|
||||
fmt::print(" location c{}h{} not found\n",
|
||||
location.cylinder,
|
||||
location.head);
|
||||
continue;
|
||||
}
|
||||
TrackFluxProto* outtrack =
|
||||
findOrMakeTrack(outf, location.cylinder, location.head);
|
||||
fmt::print(" copying c{}h{}\n", location.cylinder, location.head);
|
||||
for (const auto& flux : intrack->flux())
|
||||
outtrack->add_flux(flux);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
fmt::print("writing back output file\n");
|
||||
saveFl2File(outputFilenameFlag.get(), outf);
|
||||
}
|
||||
else
|
||||
fmt::print("output file not modified\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
45
src/fe-fluxfilels.cc
Normal file
45
src/fe-fluxfilels.cc
Normal file
@@ -0,0 +1,45 @@
|
||||
#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 StringFlag fluxFilename({"-f", "--fluxfile"}, "flux file to show");
|
||||
|
||||
int mainFluxfileLs(int argc, const char* argv[])
|
||||
{
|
||||
flags.parseFlags(argc, argv);
|
||||
if (!fluxFilename.isSet())
|
||||
error("you must specify a filename with -f");
|
||||
|
||||
fmt::print("{}:\n", fluxFilename.get());
|
||||
FluxFileProto f = loadFl2File(fluxFilename.get());
|
||||
|
||||
for (auto* s :
|
||||
{"version", "rotational_period_ms", "drive_type", "format_type"})
|
||||
fmt::print(" {}: {}\n", s, getProtoByString(&f, s));
|
||||
|
||||
for (const auto& trackFlux : f.track())
|
||||
{
|
||||
fmt::print(" flux for c{}h{}:", trackFlux.track(), trackFlux.head());
|
||||
|
||||
for (const auto& flux : trackFlux.flux())
|
||||
{
|
||||
Fluxmap fluxmap(flux);
|
||||
if (&flux != &trackFlux.flux()[0])
|
||||
fmt::print(",");
|
||||
fmt::print(" {:0.3f}ms", fluxmap.duration() / 1000000.0);
|
||||
}
|
||||
|
||||
fmt::print("\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
62
src/fe-fluxfilerm.cc
Normal file
62
src/fe-fluxfilerm.cc
Normal file
@@ -0,0 +1,62 @@
|
||||
#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/locations.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 StringFlag fluxFilename({"-f", "--fluxfile"}, "flux file to show");
|
||||
static StringFlag tracksFlag({"-t", "--tracks"}, "tracks to remove");
|
||||
|
||||
int mainFluxfileRm(int argc, const char* argv[])
|
||||
{
|
||||
flags.parseFlags(argc, argv);
|
||||
if (!fluxFilename.isSet())
|
||||
error("you must specify a filename with -f");
|
||||
|
||||
fmt::print("{}:\n", fluxFilename.get());
|
||||
FluxFileProto f = loadFl2File(fluxFilename.get());
|
||||
|
||||
bool changed = false;
|
||||
for (const auto& location : parseCylinderHeadsString(tracksFlag))
|
||||
{
|
||||
auto* repeatedFlux = f.mutable_track();
|
||||
|
||||
bool found = false;
|
||||
for (int i = 0; i < repeatedFlux->size(); i++)
|
||||
{
|
||||
const auto& trackFlux = repeatedFlux->Get(i);
|
||||
if ((trackFlux.track() == location.cylinder) &&
|
||||
(trackFlux.head() == location.head))
|
||||
{
|
||||
fmt::print(
|
||||
" removing c{}h{}\n", location.cylinder, location.head);
|
||||
repeatedFlux->DeleteSubrange(i, 1);
|
||||
found = changed = true;
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
fmt::print(" location c{}h{} not found\n",
|
||||
location.cylinder,
|
||||
location.head);
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
fmt::print("writing back file\n");
|
||||
saveFl2File(fluxFilename.get(), f);
|
||||
}
|
||||
else
|
||||
fmt::print("file not modified\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -21,9 +21,7 @@ static StringFlag sourceFlux({"--source", "-s"},
|
||||
globalConfig().setFluxSource(value);
|
||||
});
|
||||
|
||||
static IntFlag trackFlag({"--cylinder", "-c"}, "Track to read.", 0);
|
||||
|
||||
static IntFlag headFlag({"--head", "-h"}, "Head to read.", 0);
|
||||
static StringFlag destTracks({"--tracks", "-t"}, "tracks to write to", "c0h0");
|
||||
|
||||
static SettableFlag dumpFluxFlag(
|
||||
{"--dump-flux", "-F"}, "Dump raw magnetic disk flux.");
|
||||
@@ -135,7 +133,10 @@ int mainInspect(int argc, const char* argv[])
|
||||
flags.parseFlagsWithConfigFiles(argc, argv, {});
|
||||
|
||||
auto fluxSource = FluxSource::create(globalConfig());
|
||||
const auto fluxmap = fluxSource->readFlux(trackFlag, headFlag)->next();
|
||||
auto tracks = parseCylinderHeadsString(destTracks);
|
||||
if (tracks.size() != 1)
|
||||
error("you must specify exactly one track");
|
||||
const auto fluxmap = fluxSource->readFlux(tracks[0])->next();
|
||||
|
||||
std::cout << fmt::format("0x{:x} bytes of data in {:.3f}ms\n",
|
||||
fluxmap->bytes(),
|
||||
|
||||
@@ -16,7 +16,7 @@ static StringFlag sourceFlux({"-s", "--source"},
|
||||
globalConfig().setFluxSource(value);
|
||||
});
|
||||
|
||||
static IntFlag track({"--cylinder", "-c"}, "track to seek to", 0);
|
||||
static IntFlag track({"--cylinder", "-t"}, "track to seek to", 0);
|
||||
|
||||
extern const std::map<std::string, std::string> readables;
|
||||
|
||||
|
||||
@@ -7,6 +7,9 @@ typedef int command_cb(int agrc, const char* argv[]);
|
||||
extern command_cb mainAnalyseDriveResponse;
|
||||
extern command_cb mainAnalyseLayout;
|
||||
extern command_cb mainConvert;
|
||||
extern command_cb mainFluxfileLs;
|
||||
extern command_cb mainFluxfileRm;
|
||||
extern command_cb mainFluxfileCp;
|
||||
extern command_cb mainFormat;
|
||||
extern command_cb mainGetDiskInfo;
|
||||
extern command_cb mainGetFile;
|
||||
@@ -36,6 +39,7 @@ struct Command
|
||||
};
|
||||
|
||||
static command_cb mainAnalyse;
|
||||
static command_cb mainFluxfile;
|
||||
static command_cb mainTest;
|
||||
|
||||
// clang-format off
|
||||
@@ -45,6 +49,7 @@ static std::vector<Command> commands =
|
||||
{ "analyse", mainAnalyse, "Disk and drive analysis tools." },
|
||||
{ "read", mainRead, "Reads a disk, producing a sector image.", },
|
||||
{ "write", mainWrite, "Writes a sector image to a disk.", },
|
||||
{ "fluxfile", mainFluxfile, "Flux file manipulation operations.", },
|
||||
{ "format", mainFormat, "Format a disk and make a file system on it.", },
|
||||
{ "rawread", mainRawRead, "Reads raw flux from a disk. Warning: you can't use this to copy disks.", },
|
||||
{ "rawwrite", mainRawWrite, "Writes a flux file to a disk. Warning: you can't use this to copy disks.", },
|
||||
@@ -71,9 +76,16 @@ static std::vector<Command> analysables =
|
||||
|
||||
static std::vector<Command> testables =
|
||||
{
|
||||
{ "bandwidth", mainTestBandwidth, "Measures your USB bandwidth.", },
|
||||
{ "devices", mainTestDevices, "Displays all detected devices.", },
|
||||
{ "voltages", mainTestVoltages, "Measures the FDD bus voltages.", },
|
||||
{ "bandwidth", mainTestBandwidth, "Measures your USB bandwidth.", },
|
||||
{ "devices", mainTestDevices, "Displays all detected devices.", },
|
||||
{ "voltages", mainTestVoltages, "Measures the FDD bus voltages.", },
|
||||
};
|
||||
|
||||
static std::vector<Command> fluxfileables =
|
||||
{
|
||||
{ "ls", mainFluxfileLs, "Lists the contents of a flux file.", },
|
||||
{ "rm", mainFluxfileRm, "Removes flux from a flux file.", },
|
||||
{ "cp", mainFluxfileCp, "Copies flux from one flux file to another." },
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -122,6 +134,11 @@ static int mainTest(int argc, const char* argv[])
|
||||
return mainExtended(testables, "test", argc, argv);
|
||||
}
|
||||
|
||||
static int mainFluxfile(int argc, const char* argv[])
|
||||
{
|
||||
return mainExtended(fluxfileables, "fluxfile", argc, argv);
|
||||
}
|
||||
|
||||
static void globalHelp()
|
||||
{
|
||||
std::cout << "fluxengine: syntax: fluxengine <command> [<flags>...]\n"
|
||||
|
||||
0
src/fluxfile.cc
Normal file
0
src/fluxfile.cc
Normal file
4
src/fluxfile.h
Normal file
4
src/fluxfile.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
extern void parseFluxfilePath(
|
||||
const std::string& combined, std::string& filename, std::string& path);
|
||||
@@ -1,22 +0,0 @@
|
||||
comment: 'Adjust configuration for a 40-track drive'
|
||||
is_extension: true
|
||||
|
||||
documentation:
|
||||
<<<
|
||||
This is an extension profile; adding this to the command line will configure
|
||||
FluxEngine to read from 40-track, 48tpi 5.25" drives. You have to tell it because there is
|
||||
no way to detect this automatically.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
fluxengine read ibm --180 40track_drive
|
||||
```
|
||||
>>>
|
||||
|
||||
drive {
|
||||
tracks: "c0-40h0-1"
|
||||
drive_type: DRIVETYPE_40TRACK
|
||||
}
|
||||
|
||||
|
||||
77
src/formats/_global_options.textpb
Normal file
77
src/formats/_global_options.textpb
Normal file
@@ -0,0 +1,77 @@
|
||||
comment: 'Options which can be applied everywhere.'
|
||||
is_extension: true
|
||||
|
||||
option_group {
|
||||
comment: "Drive type"
|
||||
name: "drivetype"
|
||||
|
||||
option {
|
||||
name: "80"
|
||||
comment: '80 track drive'
|
||||
set_by_default: true
|
||||
|
||||
config {
|
||||
}
|
||||
}
|
||||
|
||||
option {
|
||||
name: "40"
|
||||
comment: '40 track drive'
|
||||
|
||||
config {
|
||||
drive {
|
||||
tracks: "c0-40h0-1"
|
||||
drive_type: DRIVETYPE_40TRACK
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
option {
|
||||
name: "160"
|
||||
comment: '160 track Apple II drive'
|
||||
|
||||
config {
|
||||
drive {
|
||||
tracks: "c0-159h0"
|
||||
drive_type: DRIVETYPE_APPLE2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
option_group {
|
||||
comment: 'Bus interface'
|
||||
name: "bus"
|
||||
|
||||
option {
|
||||
name: "pc"
|
||||
comment: 'PC drive interface'
|
||||
set_by_default: true
|
||||
}
|
||||
|
||||
option {
|
||||
name: "shugart"
|
||||
comment: 'Shugart bus interface (only on Greaseweazle)'
|
||||
|
||||
config {
|
||||
usb {
|
||||
greaseweazle {
|
||||
bus_type: SHUGART
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
option {
|
||||
name: "appleii"
|
||||
comment: 'Apple II bus interface (only on Greaseweazle)'
|
||||
|
||||
config {
|
||||
usb {
|
||||
greaseweazle {
|
||||
bus_type: APPLE2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
comment: 'Adjust configuration for a 40-track Apple II drive'
|
||||
is_extension: true
|
||||
|
||||
documentation:
|
||||
<<<
|
||||
This is an extension profile; adding this to the command line will configure
|
||||
FluxEngine to adjust the pinout and track spacing to work with an Apple II
|
||||
drive. This only works on Greaseweazle hardware and requires a custom
|
||||
connector.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
fluxengine read apple2 --160 apple2_drive
|
||||
```
|
||||
>>>
|
||||
|
||||
usb {
|
||||
greaseweazle {
|
||||
bus_type: APPLE2
|
||||
}
|
||||
}
|
||||
|
||||
drive {
|
||||
tracks: "c0-159h0"
|
||||
drive_type: DRIVETYPE_APPLE2
|
||||
}
|
||||
|
||||
|
||||
@@ -3,14 +3,13 @@ from build.c import cxxlibrary
|
||||
from scripts.build import protoencode
|
||||
|
||||
formats = [
|
||||
"40track_drive",
|
||||
"_global_options",
|
||||
"acornadfs",
|
||||
"acorndfs",
|
||||
"aeslanier",
|
||||
"agat",
|
||||
"amiga",
|
||||
"ampro",
|
||||
"apple2_drive",
|
||||
"apple2",
|
||||
"atarist",
|
||||
"bk",
|
||||
@@ -33,7 +32,6 @@ formats = [
|
||||
"psos",
|
||||
"rolandd20",
|
||||
"rx50",
|
||||
"shugart_drive",
|
||||
"smaky6",
|
||||
"tartu",
|
||||
"ti99",
|
||||
@@ -58,6 +56,7 @@ protoencode(
|
||||
name="formats_cc",
|
||||
srcs={name: f"./{name}.textpb" for name in formats},
|
||||
proto="ConfigProto",
|
||||
include="lib/config/config.pb.h",
|
||||
symbol="formats",
|
||||
)
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
comment: 'Adjust configuration for a Shugart drive'
|
||||
is_extension: true
|
||||
|
||||
documentation:
|
||||
<<<
|
||||
This is an extension profile; adding this to the command line will configure
|
||||
FluxEngine to adjust the pinout to work with a Shugart drive. This only works
|
||||
on Greaseweazle hardware.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
fluxengine read ibm --720 shugart_drive
|
||||
```
|
||||
>>>
|
||||
|
||||
usb {
|
||||
greaseweazle {
|
||||
bus_type: SHUGART
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,13 +57,13 @@ cxxprogram(
|
||||
|
||||
if config.osx:
|
||||
simplerule(
|
||||
name="fluxengine_pkg",
|
||||
name="fluxengine_app_zip",
|
||||
ins=[
|
||||
".+gui",
|
||||
"extras+fluxengine_icns",
|
||||
"extras+fluxengine_template",
|
||||
],
|
||||
outs=["=FluxEngine.pkg"],
|
||||
outs=["=FluxEngine.app.zip"],
|
||||
commands=[
|
||||
"rm -rf $[outs[0]]",
|
||||
"unzip -q $[ins[2]]", # creates FluxEngine.app
|
||||
@@ -79,7 +79,20 @@ if config.osx:
|
||||
"cp $$(brew --prefix abseil)/LICENSE FluxEngine.app/Contents/libs/abseil.txt",
|
||||
"cp $$(brew --prefix libtiff)/LICENSE.md FluxEngine.app/Contents/libs/libtiff.txt",
|
||||
"cp $$(brew --prefix zstd)/LICENSE FluxEngine.app/Contents/libs/zstd.txt",
|
||||
"pkgbuild --quiet --install-location /Applications --component FluxEngine.app $[outs[0]]",
|
||||
"zip -rq $[outs[0]] FluxEngine.app",
|
||||
],
|
||||
label="MKAPP",
|
||||
)
|
||||
|
||||
simplerule(
|
||||
name="fluxengine_pkg",
|
||||
ins=[
|
||||
".+fluxengine_app_zip",
|
||||
],
|
||||
outs=["=FluxEngine.pkg"],
|
||||
commands=[
|
||||
"unzip -q $[ins[0]]",
|
||||
"pkgbuild --quiet --install-location /Applications --component FluxEngine.app $[outs[0]]",
|
||||
],
|
||||
label="MKPKG",
|
||||
)
|
||||
|
||||
@@ -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",
|
||||
)
|
||||
|
||||
|
||||
@@ -40,44 +40,39 @@ tests = [
|
||||
"vfs",
|
||||
]
|
||||
|
||||
protoencode_single(
|
||||
name="testproto_cc",
|
||||
srcs=["./testproto.textpb"],
|
||||
proto="TestProto",
|
||||
include="tests/testproto.pb.h",
|
||||
symbol="testproto_pb",
|
||||
)
|
||||
|
||||
export(
|
||||
name="tests",
|
||||
deps=[
|
||||
test(
|
||||
name="proto_test",
|
||||
name=f"{n}_test",
|
||||
command=cxxprogram(
|
||||
name="proto_test_exe",
|
||||
name=f"{n}_test_exe",
|
||||
srcs=[
|
||||
"./proto.cc",
|
||||
protoencode_single(
|
||||
name="testproto_cc",
|
||||
srcs=["./testproto.textpb"],
|
||||
proto="TestProto",
|
||||
symbol="testproto_pb",
|
||||
),
|
||||
f"./{n}.cc",
|
||||
".+testproto_cc",
|
||||
],
|
||||
deps=[
|
||||
"lib/external+fl2_proto_lib",
|
||||
"+fmt_lib",
|
||||
"+protobuf_lib",
|
||||
"+protocol",
|
||||
"+z_lib",
|
||||
".+test_proto_lib",
|
||||
"dep/adflib",
|
||||
"dep/agg",
|
||||
"dep/fatfs",
|
||||
"dep/hfsutils",
|
||||
"dep/libusbp",
|
||||
"dep/snowhouse",
|
||||
"dep/stb",
|
||||
"lib/config",
|
||||
"lib/core",
|
||||
"lib/data",
|
||||
"lib/fluxsource+proto_lib",
|
||||
"src/formats",
|
||||
"dep/alphanum",
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
for n in ["proto"]
|
||||
]
|
||||
+ [
|
||||
test(
|
||||
|
||||
@@ -55,14 +55,14 @@ static void test_option_validity()
|
||||
}
|
||||
)M");
|
||||
|
||||
AssertThat(
|
||||
globalConfig().isOptionValid(globalConfig().findOption("option1")),
|
||||
AssertThat(globalConfig().isOptionValid(
|
||||
*globalConfig().findOption("option1").option),
|
||||
Equals(true));
|
||||
AssertThat(
|
||||
globalConfig().isOptionValid(globalConfig().findOption("option2")),
|
||||
AssertThat(globalConfig().isOptionValid(
|
||||
*globalConfig().findOption("option2").option),
|
||||
Equals(false));
|
||||
AssertThat(
|
||||
globalConfig().isOptionValid(globalConfig().findOption("option3")),
|
||||
AssertThat(globalConfig().isOptionValid(
|
||||
*globalConfig().findOption("option3").option),
|
||||
Equals(true));
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "lib/config/config.pb.h"
|
||||
#include "lib/config/proto.h"
|
||||
#include "snowhouse/snowhouse.h"
|
||||
#include "dep/alphanum/alphanum.h"
|
||||
#include <google/protobuf/text_format.h>
|
||||
#include <assert.h>
|
||||
#include <regex>
|
||||
@@ -28,8 +29,9 @@ static void test_setting(void)
|
||||
setProtoByString(&config, "d", "5.5");
|
||||
setProtoByString(&config, "f", "6.7");
|
||||
setProtoByString(&config, "m.s", "string");
|
||||
setProtoByString(&config, "r.s", "val1");
|
||||
setProtoByString(&config, "r.s", "val2");
|
||||
setProtoByString(&config, "r[0].s", "val1");
|
||||
setProtoByString(&config, "r[0].s", "val2");
|
||||
setProtoByString(&config, "r[1].s", "val3");
|
||||
setProtoByString(&config, "firstoption.s", "1");
|
||||
setProtoByString(&config, "secondoption.s", "2");
|
||||
|
||||
@@ -48,6 +50,9 @@ static void test_setting(void)
|
||||
r {
|
||||
s: "val2"
|
||||
}
|
||||
r {
|
||||
s: "val3"
|
||||
}
|
||||
secondoption {
|
||||
s: "2"
|
||||
}
|
||||
@@ -70,8 +75,14 @@ static void test_getting(void)
|
||||
r {
|
||||
s: "val2"
|
||||
}
|
||||
r {
|
||||
s: "val3"
|
||||
}
|
||||
secondoption {
|
||||
s: "2"
|
||||
r: 0
|
||||
r: 1
|
||||
r: 2
|
||||
}
|
||||
)M";
|
||||
|
||||
@@ -86,9 +97,11 @@ static void test_getting(void)
|
||||
AssertThat(getProtoByString(&tp, "d"), Equals("5.5"));
|
||||
AssertThat(getProtoByString(&tp, "f"), Equals("6.7"));
|
||||
AssertThat(getProtoByString(&tp, "m.s"), Equals("string"));
|
||||
AssertThat(getProtoByString(&tp, "r.s"), Equals("val2"));
|
||||
AssertThat(getProtoByString(&tp, "r[0].s"), Equals("val2"));
|
||||
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"));
|
||||
}
|
||||
|
||||
@@ -131,8 +144,30 @@ static void test_load(void)
|
||||
static void test_fields(void)
|
||||
{
|
||||
TestProto proto;
|
||||
auto fields = findAllProtoFields(&proto);
|
||||
AssertThat(fields.size(), Equals(14));
|
||||
auto fields = findAllPossibleProtoFields(proto.GetDescriptor());
|
||||
std::vector<std::string> fieldNames;
|
||||
for (const auto& e : fields)
|
||||
fieldNames.push_back(e.first);
|
||||
|
||||
AssertThat(fieldNames,
|
||||
Equals(std::vector<std::string>{"d",
|
||||
"f",
|
||||
"firstoption",
|
||||
"firstoption.r[]",
|
||||
"firstoption.s",
|
||||
"i32",
|
||||
"i64",
|
||||
"m",
|
||||
"m.r[]",
|
||||
"m.s",
|
||||
"r[]",
|
||||
"r[].r[]",
|
||||
"r[].s",
|
||||
"secondoption",
|
||||
"secondoption.r[]",
|
||||
"secondoption.s",
|
||||
"u32",
|
||||
"u64"}));
|
||||
}
|
||||
|
||||
static void test_options(void)
|
||||
@@ -145,6 +180,52 @@ static void test_options(void)
|
||||
AssertThat(s, Equals("i64"));
|
||||
}
|
||||
|
||||
static void test_findallfields(void)
|
||||
{
|
||||
std::string s = R"M(
|
||||
i64: -1
|
||||
i32: -2
|
||||
u64: 3
|
||||
u32: 4
|
||||
d: 5.5
|
||||
f: 6.7
|
||||
m {
|
||||
s: "string"
|
||||
}
|
||||
r {
|
||||
s: "val2"
|
||||
}
|
||||
r {
|
||||
s: "val3"
|
||||
}
|
||||
secondoption {
|
||||
s: "2"
|
||||
}
|
||||
)M";
|
||||
|
||||
TestProto proto;
|
||||
if (!google::protobuf::TextFormat::MergeFromString(cleanup(s), &proto))
|
||||
error("couldn't load test proto");
|
||||
|
||||
auto fields = findAllProtoFields(&proto);
|
||||
std::vector<std::string> fieldNames;
|
||||
for (const auto& e : fields)
|
||||
fieldNames.push_back(e.path());
|
||||
std::ranges::sort(fieldNames, doj::alphanum_less<std::string>());
|
||||
|
||||
AssertThat(fieldNames,
|
||||
Equals(std::vector<std::string>{"d",
|
||||
"f",
|
||||
"i32",
|
||||
"i64",
|
||||
"m.s",
|
||||
"r[0].s",
|
||||
"r[1].s",
|
||||
"secondoption.s",
|
||||
"u32",
|
||||
"u64"}));
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
try
|
||||
@@ -155,6 +236,7 @@ int main(int argc, const char* argv[])
|
||||
test_load();
|
||||
test_fields();
|
||||
test_options();
|
||||
test_findallfields();
|
||||
}
|
||||
catch (const ErrorException& e)
|
||||
{
|
||||
|
||||
@@ -2,23 +2,25 @@ syntax = "proto2";
|
||||
|
||||
import "lib/config/common.proto";
|
||||
|
||||
message TestProto {
|
||||
message SubMessageProto {
|
||||
optional string s = 1;
|
||||
}
|
||||
message TestProto
|
||||
{
|
||||
message SubMessageProto
|
||||
{
|
||||
optional string s = 1;
|
||||
repeated int32 r = 2;
|
||||
}
|
||||
|
||||
optional int64 i64 = 1 [(help)="i64"];
|
||||
optional int32 i32 = 2;
|
||||
optional uint64 u64 = 3;
|
||||
optional uint32 u32 = 4;
|
||||
optional double d = 5;
|
||||
optional double f = 11;
|
||||
optional SubMessageProto m = 6;
|
||||
repeated SubMessageProto r = 7;
|
||||
optional int64 i64 = 1 [(help) = "i64"];
|
||||
optional int32 i32 = 2;
|
||||
optional uint64 u64 = 3;
|
||||
optional uint32 u32 = 4;
|
||||
optional double d = 5;
|
||||
optional double f = 11;
|
||||
optional SubMessageProto m = 6;
|
||||
repeated SubMessageProto r = 7;
|
||||
|
||||
oneof alt {
|
||||
SubMessageProto firstoption = 8;
|
||||
SubMessageProto secondoption = 9;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user