1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/utility/media_galleries/picasa_album_table_reader.h" 6 7#include <algorithm> 8#include <string> 9 10#include "base/path_service.h" 11#include "base/strings/string_util.h" 12#include "base/strings/utf_string_conversions.h" 13#include "chrome/common/media_galleries/pmp_constants.h" 14#include "chrome/utility/media_galleries/pmp_column_reader.h" 15 16namespace picasa { 17 18namespace { 19 20// |variant_time| is specified as the number of days from Dec 30, 1899. 21base::Time TimeFromMicrosoftVariantTime(double variant_time) { 22 base::TimeDelta variant_delta = base::TimeDelta::FromMicroseconds( 23 static_cast<int64>(variant_time * base::Time::kMicrosecondsPerDay)); 24 25 return base::Time::FromLocalExploded(kPmpVariantTimeEpoch) + variant_delta; 26} 27 28} // namespace 29 30PicasaAlbumTableReader::PicasaAlbumTableReader(AlbumTableFiles table_files) 31 : table_files_(table_files.Pass()), 32 initialized_(false) { 33} 34 35PicasaAlbumTableReader::~PicasaAlbumTableReader() { 36} 37 38const std::vector<AlbumInfo>& PicasaAlbumTableReader::folders() const { 39 DCHECK(initialized_); 40 return folders_; 41} 42 43const std::vector<AlbumInfo>& PicasaAlbumTableReader::albums() const { 44 DCHECK(initialized_); 45 return albums_; 46} 47 48bool PicasaAlbumTableReader::Init() { 49 if (initialized_) 50 return true; 51 52 if (!table_files_.indicator_file.IsValid()) 53 return false; 54 55 PmpColumnReader category_column, date_column, filename_column, name_column, 56 token_column, uid_column; 57 if (!category_column.ReadFile(&table_files_.category_file, PMP_TYPE_UINT32) || 58 !date_column.ReadFile(&table_files_.date_file, PMP_TYPE_DOUBLE64) || 59 !filename_column.ReadFile(&table_files_.filename_file, PMP_TYPE_STRING) || 60 !name_column.ReadFile(&table_files_.name_file, PMP_TYPE_STRING) || 61 !token_column.ReadFile(&table_files_.token_file, PMP_TYPE_STRING) || 62 !uid_column.ReadFile(&table_files_.uid_file, PMP_TYPE_STRING)) { 63 return false; 64 } 65 66 // In the PMP format, columns can be different lengths. The number of rows 67 // in the table is max of all the columns, and short columns are NULL padded. 68 uint32 row_count = 0; 69 row_count = std::max(row_count, category_column.rows_read()); 70 row_count = std::max(row_count, date_column.rows_read()); 71 row_count = std::max(row_count, filename_column.rows_read()); 72 row_count = std::max(row_count, name_column.rows_read()); 73 row_count = std::max(row_count, token_column.rows_read()); 74 row_count = std::max(row_count, uid_column.rows_read()); 75 76 for (uint32 i = 0; i < row_count; i++) { 77 uint32 category = kAlbumCategoryInvalid; 78 double date = 0; 79 std::string name; 80 std::string uid; 81 // PMP tables often contain 'garbage' rows of deleted or auto-generated 82 // album-like entities. We ignore those rows. 83 if (!category_column.ReadUInt32(i, &category) || 84 !date_column.ReadDouble64(i, &date) || 85 !name_column.ReadString(i, &name) || name.empty() || 86 !uid_column.ReadString(i, &uid) || uid.empty()) { 87 continue; 88 } 89 90 base::Time timestamp = TimeFromMicrosoftVariantTime(date); 91 92 if (category == kAlbumCategoryAlbum) { 93 std::string token; 94 if (!token_column.ReadString(i, &token) || token.empty() || 95 !StartsWithASCII(token, kAlbumTokenPrefix, false)) { 96 continue; 97 } 98 99 albums_.push_back(AlbumInfo(name, timestamp, uid, base::FilePath())); 100 } else if (category == kAlbumCategoryFolder) { 101 std::string filename; 102 if (!filename_column.ReadString(i, &filename) || filename.empty()) 103 continue; 104 105 base::FilePath path = 106 base::FilePath(base::FilePath::FromUTF8Unsafe(filename)); 107 108 folders_.push_back(AlbumInfo(name, timestamp, uid, path)); 109 } 110 } 111 112 initialized_ = true; 113 return true; 114} 115 116} // namespace picasa 117