Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for nano 7G on-the-go playlists #7

Merged
merged 3 commits into from Dec 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion foo_dop/bplist.h
Expand Up @@ -233,7 +233,7 @@ namespace bplist
size = pfc::downcast_guarded<t_size>(reader.read_sized_int_bendian(1<< (temp&0x0f), m_abort));
}
t_size i;
object->m_array.set_count(size);
object->m_array.resize(size);
for (i=0; i<size; i++)
{
t_uint64 indexchild = reader.read_sized_int_bendian(m_footer.objectRefSize, m_abort);
Expand Down
27 changes: 25 additions & 2 deletions foo_dop/cfobject.cpp
Expand Up @@ -64,6 +64,28 @@ namespace cfobject
p_value = (child->m_integer);
return b_ret;
}

const pfc::string_simple_t<wchar_t>& object_t::dictionary::get_child_string_strict(const wchar_t * key)
{
return get_child_strict<objectType::kTagUnicodeString>(key);
}

bool object_t::dictionary::get_child_bool_strict(const wchar_t * key, std::optional<bool> default_value)
{
return get_child_strict_with_default<objectType::kTagBoolean>(key, default_value);
}

int64_t object_t::dictionary::get_child_int64_strict(const wchar_t * key, std::optional<int64_t> default_value)
{
return get_child_strict_with_default<objectType::kTagInt>(key, default_value);
}

const std::vector<object_t::ptr_t>& object_t::dictionary::get_child_array_strict(const wchar_t * key)
{
return get_child_strict<objectType::kTagArray>(key);
}


void object_t::dictionary::ensure_sorted()
{
if (!m_sorted)
Expand Down Expand Up @@ -95,7 +117,7 @@ namespace cfobject
case kTagArray:
{
p_out << "{\r\n";
for (t_size i = 0, count = ptr->m_array.get_count(); i<count; i++)
for (t_size i = 0, count = ptr->m_array.size(); i<count; i++)
{
g_print_object(ptr->m_array[i], p_out);
p_out << "\r\n";
Expand Down Expand Up @@ -153,7 +175,7 @@ namespace cfobject
case kTagArray:
{
p_out << "<array>\n";
for (t_size i = 0, count = ptr->m_array.get_count(); i<count; i++)
for (t_size i = 0, count = ptr->m_array.size(); i<count; i++)
{
g_export_object_to_inner_xml(ptr->m_array[i], p_out);
}
Expand Down Expand Up @@ -198,6 +220,7 @@ namespace cfobject
};
}
}

void g_export_object_to_xml (const object_t::ptr_t & ptr, pfc::string8 & p_out)
{
p_out.reset();
Expand Down
81 changes: 77 additions & 4 deletions foo_dop/cfobject.h
Expand Up @@ -20,6 +20,37 @@ namespace cfobject
public:
typedef pfc::refcounted_object_ptr_t<object_t> ptr_t;

template <objectType TypeTag>
auto& get_value_strict()
{
if (m_type != TypeTag) {
throw exception_io_unsupported_format();
}

if constexpr (TypeTag == kTagInt)
return m_integer;

if constexpr (TypeTag == kTagArray)
return m_array;

if constexpr (TypeTag == kTagBoolean)
return m_boolean;

if constexpr (TypeTag == kTagDictionary)
return m_dictionary;

if constexpr (TypeTag == kTagUnicodeString)
return m_string;

if constexpr (TypeTag == kTagData)
return m_data;

if constexpr (TypeTag == kTagReal)
return m_float;

throw pfc::exception_bug_check();
}

class dictionary_entry_t
{
public:
Expand Down Expand Up @@ -48,6 +79,48 @@ namespace cfobject
bool get_child (const wchar_t * key, t_uint64 & p_value);
bool get_child (const wchar_t * key, t_int64 & p_value);

template<objectType TypeTag>
auto* get_child(const wchar_t* key)
{
object_t::ptr_t child;
if (get_child(key, child))
return &child->get_value_strict<TypeTag>();

using ValuePtr = decltype(&child->get_value_strict<TypeTag>());
return static_cast<ValuePtr>(nullptr);
}

/** Note: This will copy the value (unlike get_child above) */
template<objectType TypeTag, class Default>
auto get_child_strict_with_default(const wchar_t* key, Default&& default_value = {})
{
using Type = std::remove_reference_t<decltype(std::declval<object_t&>().get_value_strict<TypeTag>())>;
std::optional<Type> default_value_as_optional{ std::forward<Default>(default_value) };
auto child = get_child<TypeTag>(key);

if (!child && !default_value_as_optional)
throw exception_io_unsupported_format();

if (!child)
return default_value_as_optional.value();

return *child;
}

template<objectType TypeTag>
auto& get_child_strict(const wchar_t* key)
{
auto child = get_child<TypeTag>(key);
if (!child)
throw exception_io_unsupported_format();
return *child;
}

const pfc::string_simple_t<wchar_t>& get_child_string_strict(const wchar_t * key);
bool get_child_bool_strict(const wchar_t * key, std::optional<bool> default_value = {});
int64_t get_child_int64_strict(const wchar_t * key, std::optional<int64_t> default_value = {});
const std::vector<object_t::ptr_t>& get_child_array_strict(const wchar_t * key);

dictionary() : m_sorted(false) {};
private:
void ensure_sorted();
Expand All @@ -59,11 +132,11 @@ namespace cfobject
bool m_boolean;
t_int64 m_integer;
double m_float;
pfc::string_simple_t<WCHAR> m_string;
pfc::string_simple_t<WCHAR> m_key;
pfc::list_t<object_t::ptr_t> m_array;
pfc::string_simple_t<wchar_t> m_string;
pfc::string_simple_t<wchar_t> m_key;
std::vector<object_t::ptr_t> m_array;
dictionary m_dictionary;
pfc::array_t<t_uint8> m_data;
pfc::array_t<std::uint8_t> m_data;
t_filetimestamp m_date;

bool get_bool() {return m_boolean || m_integer;}
Expand Down
1 change: 1 addition & 0 deletions foo_dop/foo_dop.vcxproj
Expand Up @@ -225,6 +225,7 @@
<ClCompile Include="reader_cache.cpp" />
<ClCompile Include="reader_dopdb.cpp" />
<ClCompile Include="reader_playcounts.cpp" />
<ClCompile Include="reader_playlists.cpp" />
<ClCompile Include="reader_purchases.cpp" />
<ClCompile Include="remove_files.cpp" />
<ClCompile Include="results.cpp" />
Expand Down
3 changes: 3 additions & 0 deletions foo_dop/foo_dop.vcxproj.filters
Expand Up @@ -407,6 +407,9 @@
<ClCompile Include="device_info.cpp">
<Filter>Component</Filter>
</ClCompile>
<ClCompile Include="reader_playlists.cpp">
<Filter>Backend Operations</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Image Include="folder.ico">
Expand Down
1 change: 1 addition & 0 deletions foo_dop/itunesdb.h
Expand Up @@ -20,6 +20,7 @@

t_uint32 apple_time_from_filetime(t_filetimestamp filetime, bool b_local = true);
t_filetimestamp filetime_time_from_appletime(t_uint32 appletime, bool b_convert_to_utc = true);
t_uint32 current_hfs_plus_timestamp();

bool g_print_meta(const file_info & info, const char * field, pfc::string_base & p_out);
bool g_print_meta_noblanks(const file_info & info, const char * field, pfc::string_base & p_out);
Expand Down
8 changes: 8 additions & 0 deletions foo_dop/itunesdb_helpers.cpp
Expand Up @@ -2,6 +2,7 @@

#include "ipod_manager.h"
#include "writer_sort_helpers.h"
#include "file_adder.h"

bool is_blank(const char * str)
{
Expand Down Expand Up @@ -113,6 +114,13 @@ LocalFileTimeToFileTime2(
return SystemTimeToFileTime(&stUTC, lpFileTime);
}

t_uint32 current_hfs_plus_timestamp()
{
FILETIME ft{};
GetSystemTimeAsFileTime(&ft);
return apple_time_from_filetime(g_filetime_to_timestamp(&ft));
}

t_uint32 apple_time_from_filetime(t_filetimestamp filetime_src, bool b_local)
{
if (filetime_src == filetimestamp_invalid) return 0;
Expand Down
1 change: 1 addition & 0 deletions foo_dop/itunesdb_playlist.cpp
Expand Up @@ -161,6 +161,7 @@ namespace itunesdb {
p_pihm.read_lendian_auto_t(p_out->items[i].unk1, p_abort); //36
p_pihm.read_lendian_auto_t(p_out->items[i].unk2, p_abort); //40
p_pihm.read_lendian_auto_t(p_out->items[i].item_pid, p_abort); //44
// some 64-bit other ID at +60?
}
catch (exception_io_data_truncation const &) {};

Expand Down
4 changes: 2 additions & 2 deletions foo_dop/mobile_device_cfobject.cpp
Expand Up @@ -66,7 +66,7 @@ bool g_get_CFType_object (const in_mobile_device_api_handle_sync & api, CFTypeRe
p_out->m_type = cfobject::kTagArray;
CFArrayRef arr = (CFArrayRef)ref;
t_size i, count = api->CFArrayGetCount(arr);
p_out->m_array.set_size(count);
p_out->m_array.resize(count);
for (i=0; i<count; i++)
{
CFTypeRef child = api->CFArrayGetValueAtIndex(arr, i);
Expand Down Expand Up @@ -121,7 +121,7 @@ void g_get_sql_commands (cfobject::object_t::ptr_t const & cfobj, pfc::string_li

if (CommandSet.is_valid() && CommandSet->m_dictionary.get_child(L"Commands", Commands))
{
t_size j, count = Commands->m_array.get_count();
t_size j, count = Commands->m_array.size();
for (j=0; j<count; j++)
if (Commands->m_array[j].is_valid())
names.add_item(pfc::stringcvt::string_utf8_from_wide(Commands->m_array[j]->m_string.get_ptr()));
Expand Down
4 changes: 2 additions & 2 deletions foo_dop/mobile_device_v2.cpp
Expand Up @@ -234,7 +234,7 @@ void mobile_device_handle::initialise_international()
m_icu_context.m_trans = lockedAPI->utrans_openU_4_0(International_NameTransform->m_string, -1, 0, 0, 0, 0, &status);

lockedAPI->ucol_setAttribute_4_0(m_icu_context.m_coll, 7, 17, &status);
t_size section_count = International_SectionHeaders->m_array.get_count();
t_size section_count = International_SectionHeaders->m_array.size();
m_icu_context.m_SortSections.set_count(section_count);
t_size rollingCount = 0;
for (t_size i = 0; i<section_count; i++)
Expand All @@ -245,7 +245,7 @@ void mobile_device_handle::initialise_international()
&& International_SectionHeaders->m_array[i]->m_dictionary.get_child(L"FirstCharacterAfterLanguage", FirstCharacterAfterLanguage))
{
lockedAPI.icu_get_sort_key_bound(m_icu_context.m_coll, FirstCharacterAfterLanguage->m_string.get_ptr(), -1, true, m_icu_context.m_SortSections[i].m_FirstCharacterAfterLanguageSortKey);
t_size HeaderCount = Headers->m_array.get_count();
t_size HeaderCount = Headers->m_array.size();
m_icu_context.m_SortSections[i].m_HeaderSortKeys.set_size(HeaderCount);
for (t_size j=0; j<HeaderCount; j++)
{
Expand Down
4 changes: 2 additions & 2 deletions foo_dop/plist.cpp
Expand Up @@ -297,7 +297,7 @@ void XMLPlistParser::get_cfobject_for_key(const key_t & key, cfobject::object_t:
key_t key_child;
if (!g_get_tag_v2(ptr, key_child)) break;
get_cfobject_for_key(key_child, temp);
p_out->m_array.add_item(temp);
p_out->m_array.emplace_back(std::move(temp));
}
}
break;
Expand Down Expand Up @@ -346,7 +346,7 @@ void XMLPlistParser::get_cfobject_for_key(const key_t & key, cfobject::object_t:

void g_get_checkpoint_artwork_format_single(cfobject::object_t::ptr_t const & AlbumArt, pfc::list_t<artwork_format_t> & p_out)
{
t_size i, count = AlbumArt->m_array.get_count();
t_size i, count = AlbumArt->m_array.size();
for (i=0; i<count; i++)
{
if (!AlbumArt->m_array[i].is_valid()) break;
Expand Down
1 change: 1 addition & 0 deletions foo_dop/reader.cpp
Expand Up @@ -590,6 +590,7 @@ namespace ipod

if (!m_writing || !p_ipod->mobile)
read_playcounts(p_ipod, p_abort);
load_device_playlists(p_ipod, p_abort);

p_status.checkpoint();

Expand Down
38 changes: 34 additions & 4 deletions foo_dop/reader.h
Expand Up @@ -16,6 +16,17 @@ namespace ipod

namespace tasks
{
struct DevicePlaylist {
int64_t playlist_persistent_id{};
int64_t version{};
bool playlist_deleted{};
int64_t saved_index{};
pfc::string8 name;
/** Appears to be the timestamp of iTunesDB (and not iTunesCDB or an .itdb file) */
int64_t db_timestamp_mac_os_date{};
std::vector<int64_t> track_persistent_ids;
};

class load_database_t
{
class portable_device_playbackdata_notifier_impl : public dop::portable_device_playbackdata_notifier_t
Expand Down Expand Up @@ -101,7 +112,19 @@ namespace ipod
void read_storepurchases(ipod_device_ptr_ref_t p_ipod, abort_callback & p_abort);
void read_storepurchases(ipod_device_ptr_ref_t p_ipod, store_purchases_type_t, abort_callback & p_abort);
void read_playcounts(ipod_device_ptr_ref_t p_ipod, abort_callback & p_abort);
static int g_compare_playlist_id (const pfc::rcptr_t<t_playlist> & item1, const pfc::rcptr_t<t_playlist> & item2)

/**
* For, at least, the nano 7G.
*/
void load_device_playlists(ipod_device_ptr_ref_t p_ipod, abort_callback & p_abort);

/**
* For, at least, the nano 7G.
*/
void load_device_playlist(const DevicePlaylist& playlist);
void clean_up_device_playlists();

static int g_compare_playlist_id (const pfc::rcptr_t<t_playlist> & item1, const pfc::rcptr_t<t_playlist> & item2)
{
return pfc::compare_t(item1->id,item2->id);
}
Expand All @@ -122,13 +145,19 @@ namespace ipod
return pfc::compare_t(item1->pid,dbid);
}

bool have_track (t_uint64 pid)
pfc::rcptr_t <t_track> get_track_by_pid(t_uint64 pid)
{
for (t_size i = 0, count = m_tracks.get_count(); i<count; i++)
{
if (m_tracks[i]->pid == pid) return true;
if (m_tracks[i]->pid == pid)
return m_tracks[i];
}
return false;
return {};
}

bool have_track(t_uint64 pid)
{
return get_track_by_pid(pid).is_valid();
};

void glue_items (t_size start);
Expand Down Expand Up @@ -399,6 +428,7 @@ namespace ipod
private:
bool m_failed;
bool m_writing;
pfc::list_t< pfc::string8 > m_read_device_playlists;
};
}
}
Expand Down
2 changes: 1 addition & 1 deletion foo_dop/reader_playcounts.cpp
Expand Up @@ -124,7 +124,7 @@ namespace ipod
{
if (root->m_dictionary.get_child(L"tracks", tracks))
{
t_size j, jcount = tracks->m_array.get_count();
t_size j, jcount = tracks->m_array.size();
mobilecounts.set_size(jcount);
for (j = 0; j<jcount; j++)
{
Expand Down