Skip to content

Commit

Permalink
Merge pull request #7 from reupen/nano-7g-playlists
Browse files Browse the repository at this point in the history
Add support for nano 7G on-the-go playlists
  • Loading branch information
reupen authored Dec 12, 2018
2 parents e8d1f6f + d25b3cf commit ab52ec7
Show file tree
Hide file tree
Showing 21 changed files with 301 additions and 21 deletions.
2 changes: 1 addition & 1 deletion columns_ui-sdk
2 changes: 1 addition & 1 deletion foo_dop/bplist.h
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Loading

0 comments on commit ab52ec7

Please sign in to comment.