diff --git a/firmware/application/file.cpp b/firmware/application/file.cpp index 6ba485d9..e7fec84e 100644 --- a/firmware/application/file.cpp +++ b/firmware/application/file.cpp @@ -139,18 +139,12 @@ static std::filesystem::path find_last_file_matching_pattern(const std::filesyst return last_match; } -static std::filesystem::path remove_filename_extension(const std::filesystem::path& filename) { - const auto extension_index = filename.find_last_of('.'); - return filename.substr(0, extension_index); -} - -static std::filesystem::path increment_filename_stem_ordinal(const std::filesystem::path& filename_stem) { - std::filesystem::path result { filename_stem }; - - auto it = result.rbegin(); +static std::filesystem::path increment_filename_stem_ordinal(std::filesystem::path path) { + auto t = path.replace_extension().native(); + auto it = t.rbegin(); // Increment decimal number before the extension. - for(; it != result.rend(); ++it) { + for(; it != t.rend(); ++it) { const auto c = *it; if( c < '0' ) { return { }; @@ -164,19 +158,18 @@ static std::filesystem::path increment_filename_stem_ordinal(const std::filesyst } } - return result; + return t; } -std::filesystem::path next_filename_stem_matching_pattern(const std::filesystem::path& filename_stem_pattern) { - const auto filename = find_last_file_matching_pattern(filename_stem_pattern + u".*"); - auto filename_stem = remove_filename_extension(filename); - if( filename_stem.empty() ) { - filename_stem = filename_stem_pattern; - std::replace(std::begin(filename_stem), std::end(filename_stem), '?', '0'); +std::filesystem::path next_filename_stem_matching_pattern(std::filesystem::path filename_pattern) { + const auto next_filename = find_last_file_matching_pattern(filename_pattern.replace_extension(u".*")); + if( next_filename.empty() ) { + auto pattern_s = filename_pattern.replace_extension().native(); + std::replace(std::begin(pattern_s), std::end(pattern_s), '?', '0'); + return pattern_s; } else { - filename_stem = increment_filename_stem_ordinal(filename_stem); + return increment_filename_stem_ordinal(next_filename); } - return filename_stem; } namespace std { @@ -212,9 +205,54 @@ std::string filesystem_error::what() const { } } -std::string path_to_string(const path& p) { +path path::extension() const { + const auto t = filename().native(); + const auto index = t.find_last_of(u'.'); + if( index == t.npos ) { + return { }; + } else { + return t.substr(index); + } +} + +path path::filename() const { + const auto index = _s.find_last_of(preferred_separator); + if( index == _s.npos ) { + return _s; + } else { + return _s.substr(index + 1); + } +} + +path path::stem() const { + const auto t = filename().native(); + const auto index = t.find_last_of(u'.'); + if( index == t.npos ) { + return t; + } else { + return t.substr(0, index); + } +} + +std::string path::string() const { std::wstring_convert, path::value_type> conv; - return conv.to_bytes(p); + return conv.to_bytes(native()); +} + +path& path::replace_extension(const path& replacement) { + const auto t = extension().native(); + _s.erase(_s.size() - t.size()); + if( !replacement._s.empty() ) { + if( replacement._s.front() != u'.' ) { + _s += u'.'; + } + _s += replacement._s; + } + return *this; +} + +bool operator>(const path& lhs, const path& rhs) { + return lhs.native() > rhs.native(); } directory_iterator::directory_iterator( diff --git a/firmware/application/file.hpp b/firmware/application/file.hpp index 6c6ab0ee..cca7f220 100644 --- a/firmware/application/file.hpp +++ b/firmware/application/file.hpp @@ -64,14 +64,107 @@ private: uint32_t err; }; -using path = std::u16string; +struct path { + using string_type = std::u16string; + using value_type = string_type::value_type; + + static constexpr value_type preferred_separator = u'/'; + + path( + ) : _s { } + { + } + + path( + const path& p + ) : _s { p._s } + { + } + + path( + path&& p + ) : _s { std::move(p._s) } + { + } + + template + path( + const Source& source + ) : path { std::begin(source), std::end(source) } + { + } + + template + path( + InputIt first, + InputIt last + ) : _s { first, last } + { + } + + path( + const char16_t* const s + ) : _s { s } + { + } + + path( + const TCHAR* const s + ) : _s { reinterpret_cast(s) } + { + } + + path& operator=(const path& p) { + _s = p._s; + return *this; + } + + path& operator=(path&& p) { + _s = std::move(p._s); + return *this; + } + + path extension() const; + path filename() const; + path stem() const; + + bool empty() const { + return _s.empty(); + } + + const value_type* c_str() const { + return native().c_str(); + } + + const string_type& native() const { + return _s; + } + + std::string string() const; + + path& operator+=(const path& p) { + _s += p._s; + return *this; + } + + path& operator+=(const string_type& str) { + _s += str; + return *this; + } + + path& replace_extension(const path& replacement = path()); + +private: + string_type _s; +}; + +bool operator>(const path& lhs, const path& rhs); + using file_status = BYTE; static_assert(sizeof(path::value_type) == 2, "sizeof(std::filesystem::path::value_type) != 2"); static_assert(sizeof(path::value_type) == sizeof(TCHAR), "FatFs TCHAR size != std::filesystem::path::value_type"); -std::string path_to_string(const path& p); - struct space_info { static_assert(sizeof(std::uintmax_t) >= 8, "std::uintmax_t too small ((fname); }; + const std::filesystem::path path() const noexcept { return { fname }; }; }; class directory_iterator { @@ -135,7 +228,7 @@ space_info space(const path& p); } /* namespace filesystem */ } /* namespace std */ -std::filesystem::path next_filename_stem_matching_pattern(const std::filesystem::path& filename_stem_pattern); +std::filesystem::path next_filename_stem_matching_pattern(std::filesystem::path filename_stem_pattern); class File { public: diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index cc5c40f7..8572d6d7 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -81,13 +81,13 @@ void SystemStatusView::set_title(const std::string new_value) { } void SystemStatusView::on_camera() { - const auto filename_stem = next_filename_stem_matching_pattern(u"SCR_????"); - if( filename_stem.empty() ) { + auto path = next_filename_stem_matching_pattern(u"SCR_????"); + if( path.empty() ) { return; } PNGWriter png; - auto create_error = png.create(filename_stem + u".PNG"); + auto create_error = png.create(path.replace_extension(u".PNG")); if( create_error.is_valid() ) { return; } diff --git a/firmware/application/ui_record_view.cpp b/firmware/application/ui_record_view.cpp index 296952d1..0b43ed0f 100644 --- a/firmware/application/ui_record_view.cpp +++ b/firmware/application/ui_record_view.cpp @@ -238,8 +238,8 @@ void RecordView::start() { return; } - const auto filename_stem = next_filename_stem_matching_pattern(filename_stem_pattern); - if( filename_stem.empty() ) { + auto base_path = next_filename_stem_matching_pattern(filename_stem_pattern); + if( base_path.empty() ) { return; } @@ -250,9 +250,7 @@ void RecordView::start() { auto p = std::make_unique( sampling_rate ); - auto create_error = p->create( - filename_stem + u".WAV" - ); + auto create_error = p->create(base_path.replace_extension(u".WAV")); if( create_error.is_valid() ) { handle_error(create_error.value()); } else { @@ -263,16 +261,14 @@ void RecordView::start() { case FileType::RawS16: { - const auto metadata_file_error = write_metadata_file(filename_stem + u".TXT"); + const auto metadata_file_error = write_metadata_file(base_path.replace_extension(u".TXT")); if( metadata_file_error.is_valid() ) { handle_error(metadata_file_error.value()); return; } auto p = std::make_unique(); - auto create_error = p->create( - filename_stem + u".C16" - ); + auto create_error = p->create(base_path.replace_extension(u".C16")); if( create_error.is_valid() ) { handle_error(create_error.value()); } else { @@ -286,7 +282,7 @@ void RecordView::start() { }; if( writer ) { - text_record_filename.set(std::filesystem::path_to_string(filename_stem)); + text_record_filename.set(base_path.replace_extension().string()); button_record.set_bitmap(&bitmap_stop); capture_thread = std::make_unique( std::move(writer),