diff --git a/src/apps/haikudepot/HaikuDepotConstants.h b/src/apps/haikudepot/HaikuDepotConstants.h index bb72497766..9bb8a2b322 100644 --- a/src/apps/haikudepot/HaikuDepotConstants.h +++ b/src/apps/haikudepot/HaikuDepotConstants.h @@ -27,6 +27,14 @@ enum { MSG_LOG_OUT = 'lgot', }; +enum BitmapSize { + BITMAP_SIZE_16 = 1, + BITMAP_SIZE_22 = 2, + BITMAP_SIZE_32 = 3, + BITMAP_SIZE_64 = 4, + BITMAP_SIZE_ANY = 5 +}; + // when somebody rates a package, there is a numerical // rating which is expressed in a float 0 --> 5. This // is visualized by a series of colored stars. These diff --git a/src/apps/haikudepot/Jamfile b/src/apps/haikudepot/Jamfile index 9bbfa0f213..ca70cf2e06 100644 --- a/src/apps/haikudepot/Jamfile +++ b/src/apps/haikudepot/Jamfile @@ -119,8 +119,8 @@ local applicationSources = DecisionProvider.cpp FeaturedPackagesView.cpp FilterView.cpp + IconTarPtr.cpp JobStateListener.cpp - LocalIconStore.cpp LanguageModel.cpp LinkView.cpp LinkedBitmapView.cpp @@ -133,6 +133,7 @@ local applicationSources = PackageAction.cpp PackageActionHandler.cpp PackageContentsView.cpp + PackageIconTarRepository.cpp PackageInfo.cpp PackageInfoListener.cpp PackageInfoView.cpp diff --git a/src/apps/haikudepot/model/IconTarPtr.cpp b/src/apps/haikudepot/model/IconTarPtr.cpp new file mode 100644 index 0000000000..ce88270495 --- /dev/null +++ b/src/apps/haikudepot/model/IconTarPtr.cpp @@ -0,0 +1,48 @@ +/* + * Copyright 2020, Andrew Lindesay . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include "IconTarPtr.h" + + +IconTarPtr::IconTarPtr(const BString& name) + : + fName(name), + fOffsetsMask(0) +{ +} + + +IconTarPtr::~IconTarPtr() +{ +} + + +const BString& +IconTarPtr::Name() const +{ + return fName; +} + + +off_t +IconTarPtr::Offset(BitmapSize size) const +{ + return fOffsets[size]; +} + + +bool +IconTarPtr::HasOffset(BitmapSize size) const +{ + return 0 != (fOffsetsMask & (1 << size)); +} + + +void +IconTarPtr::SetOffset(BitmapSize size, off_t value) +{ + fOffsets[size] = value; + fOffsetsMask |= (1 << size); +} diff --git a/src/apps/haikudepot/model/IconTarPtr.h b/src/apps/haikudepot/model/IconTarPtr.h new file mode 100644 index 0000000000..81edcfe4bb --- /dev/null +++ b/src/apps/haikudepot/model/IconTarPtr.h @@ -0,0 +1,38 @@ +/* + * Copyright 2020, Andrew Lindesay . + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef ICON_TAR_PTR_H +#define ICON_TAR_PTR_H + +#include + +#include +#include + +#include "HaikuDepotConstants.h" + + +/*! The tar icon repository is able to find the icons for each package by + scanning the tar file for suitable icon files and recording the offsets into + the tar file when it does find a suitable icon file. This class is used + to return the offsets for a given package. +*/ + +class IconTarPtr : public BReferenceable { +public: + IconTarPtr(const BString& name); + virtual ~IconTarPtr(); + + const BString& Name() const; + bool HasOffset(BitmapSize size) const; + off_t Offset(BitmapSize size) const; + void SetOffset(BitmapSize size, off_t value); + +private: + BString fName; + uint8 fOffsetsMask; + off_t fOffsets[5]; +}; + +#endif // ICON_TAR_PTR_H \ No newline at end of file diff --git a/src/apps/haikudepot/model/LocalIconStore.cpp b/src/apps/haikudepot/model/LocalIconStore.cpp deleted file mode 100644 index a1b925355f..0000000000 --- a/src/apps/haikudepot/model/LocalIconStore.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2017-2020, Andrew Lindesay . - * All rights reserved. Distributed under the terms of the MIT License. - */ - -#include "LocalIconStore.h" - -#include - -#include -#include - -#include "Logger.h" -#include "ServerIconExportUpdateProcess.h" -#include "StorageUtils.h" - - -LocalIconStore::LocalIconStore(const BPath& path) -{ - fIconStoragePath = path; -} - - -LocalIconStore::~LocalIconStore() -{ -} - - -bool -LocalIconStore::_HasIconStoragePath() const -{ - return fIconStoragePath.InitCheck() == B_OK; -} - - -/* This method will try to find an icon for the package name supplied. If an - * icon was able to be found then the method will return B_OK and will update - * the supplied path object with the path to the icon file. - */ - -status_t -LocalIconStore::TryFindIconPath(const BString& pkgName, BPath& path) const -{ - BPath bestIconPath; - BPath iconPkgPath(fIconStoragePath); - bool exists; - bool isDir; - - if ( (iconPkgPath.Append("hicn") == B_OK) - && (iconPkgPath.Append(pkgName) == B_OK) - && (StorageUtils::ExistsObject(iconPkgPath, &exists, &isDir, NULL) - == B_OK) - && exists - && isDir - && (_IdentifyBestIconFileAtDirectory(iconPkgPath, bestIconPath) - == B_OK) - ) { - path = bestIconPath; - return B_OK; - } - - path.Unset(); - return B_FILE_NOT_FOUND; -} - - -status_t -LocalIconStore::_IdentifyBestIconFileAtDirectory(const BPath& directory, - BPath& bestIconPath) const -{ - static const char* kIconLeafnames[] = { - "icon.hvif", - "64.png", - "32.png", - "16.png", - NULL - }; - - bestIconPath.Unset(); - - for (int32 i = 0; kIconLeafnames[i] != NULL; i++) { - BPath workingPath(directory); - bool exists; - bool isDir; - - if ( (workingPath.Append(kIconLeafnames[i]) == B_OK - && StorageUtils::ExistsObject( - workingPath, &exists, &isDir, NULL) == B_OK) - && exists - && !isDir) { - bestIconPath.SetTo(workingPath.Path()); - return B_OK; - } - } - - return B_FILE_NOT_FOUND; -} diff --git a/src/apps/haikudepot/model/LocalIconStore.h b/src/apps/haikudepot/model/LocalIconStore.h deleted file mode 100644 index 008b1fcf95..0000000000 --- a/src/apps/haikudepot/model/LocalIconStore.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017, Andrew Lindesay . - * All rights reserved. Distributed under the terms of the MIT License. - */ -#ifndef LOCAL_ICON_STORE_H -#define LOCAL_ICON_STORE_H - -#include -#include -#include - -#include "PackageInfo.h" - - -class LocalIconStore { -public: - LocalIconStore(const BPath& path); - virtual ~LocalIconStore(); - status_t TryFindIconPath(const BString& pkgName, - BPath& path) const; - -private: - bool _HasIconStoragePath() const; - status_t _IdentifyBestIconFileAtDirectory( - const BPath& directory, - BPath& bestIconPath) const; - - BPath fIconStoragePath; - -}; - - -#endif // LOCAL_ICON_STORE_H diff --git a/src/apps/haikudepot/model/Model.cpp b/src/apps/haikudepot/model/Model.cpp index f30fb3c268..ebb56d947c 100644 --- a/src/apps/haikudepot/model/Model.cpp +++ b/src/apps/haikudepot/model/Model.cpp @@ -341,6 +341,24 @@ Model::Language() } +PackageIconRepository& +Model::GetPackageIconRepository() +{ + return fPackageIconRepository; +} + + +status_t +Model::InitPackageIconRepository() +{ + BPath tarPath; + status_t result = IconTarPath(tarPath); + if (result == B_OK) + result = fPackageIconRepository.Init(tarPath); + return result; +} + + bool Model::AddListener(const ModelListenerRef& listener) { @@ -906,9 +924,9 @@ Model::DumpExportReferenceDataPath(BPath& path) status_t -Model::IconStoragePath(BPath& path) const +Model::IconTarPath(BPath& path) const { - return StorageUtils::LocalWorkingDirectoryPath("__allicons", path); + return StorageUtils::LocalWorkingFilesPath("pkgicon-all.tar", path); } diff --git a/src/apps/haikudepot/model/Model.h b/src/apps/haikudepot/model/Model.h index 2ca85f72b5..8cc9c28fbd 100644 --- a/src/apps/haikudepot/model/Model.h +++ b/src/apps/haikudepot/model/Model.h @@ -9,8 +9,8 @@ #include #include "AbstractProcess.h" +#include "PackageIconTarRepository.h" #include "LanguageModel.h" -#include "LocalIconStore.h" #include "PackageInfo.h" #include "WebAppInterface.h" @@ -71,6 +71,9 @@ public: virtual ~Model(); LanguageModel* Language(); + PackageIconRepository& + GetPackageIconRepository(); + status_t InitPackageIconRepository(); BLocker* Lock() { return &fLock; } @@ -152,7 +155,7 @@ public: DepotMapper* depotMapper, void* context); - status_t IconStoragePath(BPath& path) const; + status_t IconTarPath(BPath& path) const; status_t DumpExportReferenceDataPath(BPath& path); status_t DumpExportRepositoryDataPath(BPath& path); status_t DumpExportPkgDataPath(BPath& path, @@ -206,6 +209,8 @@ private: bool fShowDevelopPackages; LanguageModel fLanguageModel; + PackageIconTarRepository + fPackageIconRepository; WebAppInterface fWebAppInterface; ModelListenerList fListeners; diff --git a/src/apps/haikudepot/model/PackageIconRepository.h b/src/apps/haikudepot/model/PackageIconRepository.h new file mode 100644 index 0000000000..3c0850012d --- /dev/null +++ b/src/apps/haikudepot/model/PackageIconRepository.h @@ -0,0 +1,22 @@ +/* + * Copyright 2020, Andrew Lindesay . + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef PACKAGE_ICON_REPOSITORY_H +#define PACKAGE_ICON_REPOSITORY_H + +#include + +#include "HaikuDepotConstants.h" +#include "SharedBitmap.h" + + +class PackageIconRepository { +public: + virtual bool HasAnyIcon(const BString& pkgName) = 0; + virtual status_t GetIcon(const BString& pkgName, BitmapSize size, + BitmapRef& bitmap) = 0; +}; + + +#endif // PACKAGE_ICON_REPOSITORY_H diff --git a/src/apps/haikudepot/model/PackageIconTarRepository.cpp b/src/apps/haikudepot/model/PackageIconTarRepository.cpp new file mode 100644 index 0000000000..e37ea76716 --- /dev/null +++ b/src/apps/haikudepot/model/PackageIconTarRepository.cpp @@ -0,0 +1,371 @@ +/* + * Copyright 2020, Andrew Lindesay . + * All rights reserved. Distributed under the terms of the MIT License. + */ +#include "PackageIconTarRepository.h" + +#include +#include +#include +#include + +#include "Logger.h" +#include "TarArchiveService.h" + + +BitmapRef +PackageIconTarRepository::sDefaultIcon(new(std::nothrow) SharedBitmap( + "application/x-vnd.haiku-package"), true); + + +/*! An instance of this class can be provided to the TarArchiveService to + be called each time a tar entry is found. This way it is able to capture + the offsets of the icons in the tar file against the package names. +*/ + +class IconTarPtrEntryListener : public TarEntryListener +{ +public: + IconTarPtrEntryListener( + PackageIconTarRepository* + fPackageIconTarRepository); + virtual ~IconTarPtrEntryListener(); + + virtual status_t Handle( + const TarArchiveHeader& header, + size_t offset, + BDataIO *data); +private: + status_t _LeafNameToBitmapSize(BString& leafName, + BitmapSize* bitmapSize); + +private: + PackageIconTarRepository* + fPackageIconTarRepository; +}; + + +IconTarPtrEntryListener::IconTarPtrEntryListener( + PackageIconTarRepository* packageIconTarRepository) + : + fPackageIconTarRepository(packageIconTarRepository) +{ +} + + +IconTarPtrEntryListener::~IconTarPtrEntryListener() +{ +} + + +/*! The format of the filenames in the archive are of the format; + \code + hicn/ardino/icon.hvif + \endcode + The leafname (the last part of the path) determines the type of the file as + it could be either in HVIF format or a PNG of various sizes. +*/ + +status_t +IconTarPtrEntryListener::Handle(const TarArchiveHeader& header, + size_t offset, BDataIO *data) +{ + if (header.FileType() != TAR_FILE_TYPE_NORMAL) + return B_OK; + + BString fileName = header.FileName(); + if (!fileName.StartsWith("hicn/")) + return B_OK; + + int32 secondSlashIdx = fileName.FindFirst("/", 5); + if (secondSlashIdx == B_ERROR || secondSlashIdx == 5) + return B_OK; + + BString packageName; + BString leafName; + fileName.CopyInto(packageName, 5, secondSlashIdx - 5); + fileName.CopyInto(leafName, secondSlashIdx + 1, + fileName.Length() - (secondSlashIdx + 1)); + BitmapSize bitmapSize; + + if (_LeafNameToBitmapSize(leafName, &bitmapSize) == B_OK) { + fPackageIconTarRepository->AddIconTarPtr(packageName, bitmapSize, + offset); + } + + return B_OK; +} + + +status_t +IconTarPtrEntryListener::_LeafNameToBitmapSize(BString& leafName, + BitmapSize* bitmapSize) +{ + if (leafName == "icon.hvif") { + *bitmapSize = BITMAP_SIZE_ANY; + return B_OK; + } + if (leafName == "64.png") { + *bitmapSize = BITMAP_SIZE_64; + return B_OK; + } + if (leafName == "32.png") { + *bitmapSize = BITMAP_SIZE_32; + return B_OK; + } + if (leafName == "16.png") { + *bitmapSize = BITMAP_SIZE_16; + return B_OK; + } + return B_BAD_VALUE; +} + + +PackageIconTarRepository::PackageIconTarRepository() + : + fTarIo(NULL) +{ +} + + +PackageIconTarRepository::~PackageIconTarRepository() +{ +} + + +/*! This method will reconfigure itself using the data in the tar file supplied. + Any existing data will be flushed and the new tar will be scanned for + offsets to usable files. +*/ + +status_t +PackageIconTarRepository::Init(BPath& tarPath) +{ + BAutolock locker(&fLock); + _Close(); + status_t result = B_OK; + + if (tarPath.Path() == NULL) { + HDINFO("empty path to tar-ball"); + result = B_BAD_VALUE; + } + + BFile *tarIo = NULL; + + if (result == B_OK) { + HDINFO("will init icon model from tar [%s]", tarPath.Path()); + tarIo = new BFile(tarPath.Path(), O_RDONLY); + + if (!tarIo->IsReadable()) { + HDERROR("unable to read the tar [%s]", tarPath.Path()); + result = B_IO_ERROR; + } + } + + // will fill the model up with records from the tar-ball. + + if (result == B_OK) { + BStopWatch watch("PackageIconTarRepository::Init", true); + HDINFO("will read [%s] and collect the tar pointers", tarPath.Path()); + + IconTarPtrEntryListener* listener = new IconTarPtrEntryListener(this); + ObjectDeleter listenerDeleter(listener); + TarArchiveService::ForEachEntry(*tarIo, listener); + + double secs = watch.ElapsedTime() / 1000000.0; + HDINFO("did collect %" B_PRIi32 " tar pointers (%6.3g secs)", + fIconTarPtrs.Size(), secs); + } + + if (result == B_OK) + fTarIo = tarIo; + else + delete tarIo; + + return result; +} + + +void +PackageIconTarRepository::_Close() +{ + delete fTarIo; + fTarIo = NULL; + fIconTarPtrs.Clear(); + fIconCache.Clear(); +} + + +/*! This method should be treated private and only called from a situation + in which the class's lock is acquired. It is used to populate data from + the parsing of the tar headers. It is called from the listener above. +*/ + +void +PackageIconTarRepository::AddIconTarPtr(const BString& packageName, + BitmapSize bitmapSize, off_t offset) +{ + IconTarPtrRef tarPtrRef = _GetOrCreateIconTarPtr(packageName); + tarPtrRef->SetOffset(bitmapSize, offset); +} + + +bool +PackageIconTarRepository::HasAnyIcon(const BString& pkgName) +{ + BAutolock locker(&fLock); + HashString key(pkgName); + return fIconTarPtrs.ContainsKey(key); +} + + +status_t +PackageIconTarRepository::GetIcon(const BString& pkgName, BitmapSize size, + BitmapRef& bitmap) +{ + BAutolock locker(&fLock); + status_t result = B_OK; + BitmapSize actualSize; + off_t iconDataTarOffset = -1; + const IconTarPtrRef tarPtrRef = _GetIconTarPtr(pkgName); + + if (tarPtrRef.Get() != NULL) { + iconDataTarOffset = _OffsetToBestRepresentation(tarPtrRef, size, + &actualSize); + } + + if (iconDataTarOffset < 0) + bitmap.SetTo(sDefaultIcon); + else { + HashString key = _ToIconCacheKey(pkgName, actualSize); + + // TODO; need to implement an LRU cache so that not too many icons are + // in memory at the same time. + + if (!fIconCache.ContainsKey(key)) { + result = _CreateIconFromTarOffset(iconDataTarOffset, bitmap); + if (result == B_OK) + fIconCache.Put(key, bitmap); + else { + HDERROR("failure to read image for package [%s] at offset %" + B_PRIdSSIZE, pkgName.String(), iconDataTarOffset); + fIconCache.Put(key, sDefaultIcon); + } + } + bitmap.SetTo(fIconCache.Get(key).Get()); + } + return result; +} + + +IconTarPtrRef +PackageIconTarRepository::_GetIconTarPtr(const BString& pkgName) const +{ + return fIconTarPtrs.Get(HashString(pkgName)); +} + + +const char* +PackageIconTarRepository::_ToIconCacheKeySuffix(BitmapSize size) +{ + switch (size) + { + case BITMAP_SIZE_16: + return "16"; + // note that size 22 is not supported. + case BITMAP_SIZE_32: + return "32"; + case BITMAP_SIZE_64: + return "64"; + case BITMAP_SIZE_ANY: + return "any"; + default: + HDERROR("unsupported bitmap size"); + exit(1); + break; + } +} + + +const HashString +PackageIconTarRepository::_ToIconCacheKey(const BString& pkgName, + BitmapSize size) +{ + return HashString(BString(pkgName) << "__x" << _ToIconCacheKeySuffix(size)); +} + + +status_t +PackageIconTarRepository::_CreateIconFromTarOffset(off_t offset, + BitmapRef& bitmap) +{ + fTarIo->Seek(offset, SEEK_SET); + TarArchiveHeader header; + status_t result = TarArchiveService::GetEntry(*fTarIo, header); + + if (result == B_OK && header.Length() <= 0) + result = B_BAD_DATA; + + if (result == B_OK) + bitmap.SetTo(new(std::nothrow)SharedBitmap(*fTarIo, header.Length())); + + return result; +} + + +/*! If there is a vector representation (HVIF) then this will be the best + option. If there are only bitmap images to choose from then consider what + the target size is and choose the best image to match. +*/ + +/*static*/ off_t +PackageIconTarRepository::_OffsetToBestRepresentation( + const IconTarPtrRef iconTarPtrRef, BitmapSize desiredSize, + BitmapSize* actualSize) +{ + if (iconTarPtrRef->HasOffset(BITMAP_SIZE_ANY)) { + *actualSize = BITMAP_SIZE_ANY; + return iconTarPtrRef->Offset(BITMAP_SIZE_ANY); + } + if (iconTarPtrRef->HasOffset(BITMAP_SIZE_64) + && desiredSize >= BITMAP_SIZE_64) { + *actualSize = BITMAP_SIZE_64; + return iconTarPtrRef->Offset(BITMAP_SIZE_64); + } + if (iconTarPtrRef->HasOffset(BITMAP_SIZE_32) + && desiredSize >= BITMAP_SIZE_32) { + *actualSize = BITMAP_SIZE_32; + return iconTarPtrRef->Offset(BITMAP_SIZE_32); + } + if (iconTarPtrRef->HasOffset(BITMAP_SIZE_22) + && desiredSize >= BITMAP_SIZE_22) { + *actualSize = BITMAP_SIZE_22; + return iconTarPtrRef->Offset(BITMAP_SIZE_22); + } + if (iconTarPtrRef->HasOffset(BITMAP_SIZE_16)) { + *actualSize = BITMAP_SIZE_16; + return iconTarPtrRef->Offset(BITMAP_SIZE_16); + } + return -1; +} + + +IconTarPtrRef +PackageIconTarRepository::_GetOrCreateIconTarPtr(const BString& pkgName) +{ + BAutolock locker(&fLock); + HashString key(pkgName); + if (!fIconTarPtrs.ContainsKey(key)) { + IconTarPtrRef value(new IconTarPtr(pkgName)); + fIconTarPtrs.Put(key, value); + return value; + } + return fIconTarPtrs.Get(key); +} + + +/*static*/ void +PackageIconTarRepository::CleanupDefaultIcon() +{ + sDefaultIcon.Unset(); +} diff --git a/src/apps/haikudepot/model/PackageIconTarRepository.h b/src/apps/haikudepot/model/PackageIconTarRepository.h new file mode 100644 index 0000000000..1ae509e4b3 --- /dev/null +++ b/src/apps/haikudepot/model/PackageIconTarRepository.h @@ -0,0 +1,65 @@ +/* + * Copyright 2020, Andrew Lindesay . + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef PACKAGE_ICON_TAR_REPOSITORY_H +#define PACKAGE_ICON_TAR_REPOSITORY_H + +#include +#include +#include +#include +#include +#include + +#include "PackageIconRepository.h" +#include "IconTarPtr.h" + +typedef BReference IconTarPtrRef; + + +class PackageIconTarRepository : public PackageIconRepository { +public: + PackageIconTarRepository(); + virtual ~PackageIconTarRepository(); + + status_t Init(BPath& tarPath); + + void AddIconTarPtr(const BString& pkgName, + BitmapSize size, off_t offset); + virtual status_t GetIcon(const BString& pkgName, BitmapSize size, + BitmapRef& bitmap); + virtual bool HasAnyIcon(const BString& pkgName); + + static void CleanupDefaultIcon(); + +private: + void _Close(); + + const char* _ToIconCacheKeySuffix(BitmapSize size); + const HashString _ToIconCacheKey(const BString& pkgName, + BitmapSize size); + + IconTarPtrRef _GetOrCreateIconTarPtr(const BString& pkgName); + IconTarPtrRef _GetIconTarPtr(const BString& pkgName) const; + + status_t _CreateIconFromTarOffset(off_t offset, + BitmapRef& bitmap); + + static off_t _OffsetToBestRepresentation( + const IconTarPtrRef iconTarPtrRef, + BitmapSize desiredSize, + BitmapSize* actualSize); + +private: + BLocker fLock; + BPositionIO* fTarIo; + HashMap + fIconCache; + HashMap + fIconTarPtrs; + + static BitmapRef sDefaultIcon; +}; + +#endif // PACKAGE_ICON_TAR_REPOSITORY_H diff --git a/src/apps/haikudepot/model/PackageInfo.cpp b/src/apps/haikudepot/model/PackageInfo.cpp index f36cee2e1f..96a724dc04 100644 --- a/src/apps/haikudepot/model/PackageInfo.cpp +++ b/src/apps/haikudepot/model/PackageInfo.cpp @@ -456,14 +456,8 @@ ScreenshotInfo::operator!=(const ScreenshotInfo& other) const // #pragma mark - PackageInfo -BitmapRef -PackageInfo::sDefaultIcon(new(std::nothrow) SharedBitmap( - "application/x-vnd.haiku-package"), true); - - PackageInfo::PackageInfo() : - fIcon(sDefaultIcon), fName(), fTitle(), fVersion(), @@ -494,7 +488,6 @@ PackageInfo::PackageInfo() PackageInfo::PackageInfo(const BPackageInfo& info) : - fIcon(sDefaultIcon), fName(info.Name()), fTitle(), fVersion(info.Version()), @@ -541,7 +534,6 @@ PackageInfo::PackageInfo(const BString& name, const BString& shortDescription, const BString& fullDescription, int32 flags, const char* architecture) : - fIcon(sDefaultIcon), fName(name), fTitle(), fVersion(version), @@ -573,7 +565,6 @@ PackageInfo::PackageInfo(const BString& name, PackageInfo::PackageInfo(const PackageInfo& other) : - fIcon(other.fIcon), fName(other.fName), fTitle(other.fTitle), fVersion(other.fVersion), @@ -607,7 +598,6 @@ PackageInfo::PackageInfo(const PackageInfo& other) PackageInfo& PackageInfo::operator=(const PackageInfo& other) { - fIcon = other.fIcon; fName = other.fName; fTitle = other.fTitle; fVersion = other.fVersion; @@ -639,8 +629,7 @@ PackageInfo::operator=(const PackageInfo& other) bool PackageInfo::operator==(const PackageInfo& other) const { - return fIcon == other.fIcon - && fName == other.fName + return fName == other.fName && fTitle == other.fTitle && fVersion == other.fVersion && fPublisher == other.fPublisher @@ -709,16 +698,6 @@ PackageInfo::SetFullDescription(const BString& description) } -void -PackageInfo::SetIcon(const BitmapRef& icon) -{ - if (fIcon != icon) { - fIcon = icon; - _NotifyListeners(PKG_CHANGED_ICON); - } -} - - void PackageInfo::SetHasChangelog(bool value) { @@ -991,9 +970,9 @@ PackageInfo::RemoveListener(const PackageInfoListenerRef& listener) void -PackageInfo::CleanupDefaultIcon() +PackageInfo::NotifyChangedIcon() { - sDefaultIcon.Unset(); + _NotifyListeners(PKG_CHANGED_ICON); } diff --git a/src/apps/haikudepot/model/PackageInfo.h b/src/apps/haikudepot/model/PackageInfo.h index 89d1fb61ed..fb74afa57d 100644 --- a/src/apps/haikudepot/model/PackageInfo.h +++ b/src/apps/haikudepot/model/PackageInfo.h @@ -283,10 +283,6 @@ public: const PublisherInfo& Publisher() const { return fPublisher; } - void SetIcon(const BitmapRef& icon); - const BitmapRef& Icon() const - { return fIcon; } - void SetHasChangelog(bool value); bool HasChangelog() const { return fHasChangelog; } @@ -367,17 +363,15 @@ public: void RemoveListener( const PackageInfoListenerRef& listener); - static void CleanupDefaultIcon(); - void StartCollatingChanges(); void EndCollatingChanges(); + void NotifyChangedIcon(); private: void _NotifyListeners(uint32 changes); void _NotifyListenersImmediate(uint32 changes); private: - BitmapRef fIcon; BString fName; BString fTitle; BPackageVersion fVersion; @@ -407,8 +401,6 @@ private: bool fIsCollatingChanges; uint32 fCollatedChanges; - - static BitmapRef sDefaultIcon; }; diff --git a/src/apps/haikudepot/server/AbstractServerProcess.cpp b/src/apps/haikudepot/server/AbstractServerProcess.cpp index 6010a705dc..d02a267d98 100644 --- a/src/apps/haikudepot/server/AbstractServerProcess.cpp +++ b/src/apps/haikudepot/server/AbstractServerProcess.cpp @@ -17,6 +17,7 @@ #include +#include "DataIOUtils.h" #include "HaikuDepotConstants.h" #include "Logger.h" #include "ServerHelper.h" @@ -112,16 +113,9 @@ AbstractServerProcess::IfModifiedSinceHeaderValue(BString& headerValue, StandardMetaData metaData; status_t result = PopulateMetaData(metaData, metaDataPath, jsonPath); - if (result == B_OK) { - - // An example of this output would be; 'Fri, 24 Oct 2014 19:32:27 +0000' - - BDateTime modifiedDateTime = metaData - .GetDataModifiedTimestampAsDateTime(); - BPrivate::BHttpTime modifiedHttpTime(modifiedDateTime); - headerValue.SetTo(modifiedHttpTime - .ToString(BPrivate::B_HTTP_TIME_FORMAT_COOKIE)); - } else { + if (result == B_OK) + SetIfModifiedSinceHeaderValueFromMetaData(headerValue, metaData); + else { HDERROR("unable to parse the meta-data date and time from [%s]" " - cannot set the 'If-Modified-Since' header", metaDataPath.Path()); @@ -131,6 +125,20 @@ AbstractServerProcess::IfModifiedSinceHeaderValue(BString& headerValue, } +/*static*/ void +AbstractServerProcess::SetIfModifiedSinceHeaderValueFromMetaData( + BString& headerValue, + const StandardMetaData& metaData) +{ + // An example of this output would be; 'Fri, 24 Oct 2014 19:32:27 +0000' + BDateTime modifiedDateTime = metaData + .GetDataModifiedTimestampAsDateTime(); + BPrivate::BHttpTime modifiedHttpTime(modifiedDateTime); + headerValue.SetTo(modifiedHttpTime + .ToString(BPrivate::B_HTTP_TIME_FORMAT_COOKIE)); +} + + status_t AbstractServerProcess::PopulateMetaData( StandardMetaData& metaData, const BPath& path, @@ -158,7 +166,7 @@ AbstractServerProcess::PopulateMetaData( /* static */ bool -AbstractServerProcess::LooksLikeGzip(const char *pathStr) +AbstractServerProcess::_LooksLikeGzip(const char *pathStr) { int l = strlen(pathStr); return l > 4 && 0 == strncmp(&pathStr[l - 3], ".gz", 3); @@ -190,7 +198,7 @@ AbstractServerProcess::ParseJsonFromFileWithListener( // compressed and the algorithm needs to decompress the data as // it is parsed. - if (LooksLikeGzip(pathStr)) { + if (_LooksLikeGzip(pathStr)) { BDataIO* gzDecompressedInput = NULL; BZlibDecompressionParameters* zlibDecompressionParameters = new BZlibDecompressionParameters(); @@ -226,10 +234,18 @@ AbstractServerProcess::DownloadToLocalFileAtomically( status_t result = DownloadToLocalFile( temporaryFilePath, url, 0, 0); + // if the data is coming in as .gz, but is not stored as .gz then + // the data should be decompressed in the temporary file location + // before being shifted into place. + + if (result == B_OK + && _LooksLikeGzip(url.Path()) + && !_LooksLikeGzip(targetFilePath.Path())) + result = _DeGzipInSitu(temporaryFilePath); + // not copying if the data has not changed because the data will be // zero length. This is if the result is APP_ERR_NOT_MODIFIED. if (result == B_OK) { - // if the file is zero length then assume that something has // gone wrong. off_t size; @@ -251,6 +267,45 @@ AbstractServerProcess::DownloadToLocalFileAtomically( } +/*static*/ status_t +AbstractServerProcess::_DeGzipInSitu(const BPath& path) +{ + const char* tmpPath = tmpnam(NULL); + status_t result = B_OK; + + { + BFile file(path.Path(), O_RDONLY); + BFile tmpFile(tmpPath, O_WRONLY | O_CREAT); + + BDataIO* gzDecompressedInput = NULL; + BZlibDecompressionParameters* zlibDecompressionParameters + = new BZlibDecompressionParameters(); + + result = BZlibCompressionAlgorithm() + .CreateDecompressingInputStream(&file, + zlibDecompressionParameters, gzDecompressedInput); + + if (result == B_OK) { + ObjectDeleter gzDecompressedInputDeleter( + gzDecompressedInput); + result = DataIOUtils::CopyAll(&tmpFile, gzDecompressedInput); + } + } + + if (result == B_OK) { + if (rename(tmpPath, path.Path()) != 0) { + HDERROR("unable to move the uncompressed data into place"); + result = B_ERROR; + } + } + else { + HDERROR("it was not possible to decompress the data"); + } + + return result; +} + + status_t AbstractServerProcess::DownloadToLocalFile(const BPath& targetFilePath, const BUrl& url, uint32 redirects, uint32 failures) diff --git a/src/apps/haikudepot/server/AbstractServerProcess.h b/src/apps/haikudepot/server/AbstractServerProcess.h index 12e7e7ee69..fd787ad14f 100644 --- a/src/apps/haikudepot/server/AbstractServerProcess.h +++ b/src/apps/haikudepot/server/AbstractServerProcess.h @@ -1,9 +1,7 @@ /* - * Copyright 2017-2018, Andrew Lindesay . + * Copyright 2017-2020, Andrew Lindesay . * All rights reserved. Distributed under the terms of the MIT License. */ - - #ifndef ABSTRACT_SERVER_PROCESS_H #define ABSTRACT_SERVER_PROCESS_H @@ -39,12 +37,15 @@ protected: virtual void GetStandardMetaDataJsonPath( BString& jsonPath) const = 0; - status_t IfModifiedSinceHeaderValue( + virtual status_t IfModifiedSinceHeaderValue( BString& headerValue) const; status_t IfModifiedSinceHeaderValue( BString& headerValue, const BPath& metaDataPath, const BString& jsonPath) const; + static void SetIfModifiedSinceHeaderValueFromMetaData( + BString& headerValue, + const StandardMetaData& metaData); status_t PopulateMetaData( StandardMetaData& metaData, @@ -55,7 +56,7 @@ protected: BJsonEventListener *listener, const BPath& path) const; - status_t DownloadToLocalFileAtomically( + virtual status_t DownloadToLocalFileAtomically( const BPath& targetFilePath, const BUrl& url); @@ -72,17 +73,18 @@ protected: virtual status_t StopInternal(); private: - uint32 fOptions; - - BHttpRequest* fRequest; - status_t DownloadToLocalFile( const BPath& targetFilePath, const BUrl& url, uint32 redirects, uint32 failures); - static bool LooksLikeGzip(const char *pathStr); + static bool _LooksLikeGzip(const char *pathStr); + static status_t _DeGzipInSitu(const BPath& path); +private: + uint32 fOptions; + + BHttpRequest* fRequest; }; #endif // ABSTRACT_SERVER_PROCESS_H diff --git a/src/apps/haikudepot/server/ServerIconExportUpdateProcess.cpp b/src/apps/haikudepot/server/ServerIconExportUpdateProcess.cpp index e0b3a29305..fcf1b50860 100644 --- a/src/apps/haikudepot/server/ServerIconExportUpdateProcess.cpp +++ b/src/apps/haikudepot/server/ServerIconExportUpdateProcess.cpp @@ -2,55 +2,106 @@ * Copyright 2017-2020, Andrew Lindesay . * All rights reserved. Distributed under the terms of the MIT License. */ - - #include "ServerIconExportUpdateProcess.h" #include #include +#include #include #include #include -#include -#include +#include "DataIOUtils.h" #include "HaikuDepotConstants.h" #include "Logger.h" #include "ServerHelper.h" -#include "ServerSettings.h" +#include "StandardMetaDataJsonEventListener.h" #include "StorageUtils.h" #include "TarArchiveService.h" +#define ENTRY_PATH_METADATA "hicn/info.json" #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "ServerIconExportUpdateProcess" -/*! This constructor will locate the cached data in a standardized location */ +/*! This listener will scan the files that are available in the tar file and + find the meta-data file. This is a JSON piece of data that describes the + timestamp of the last modified data in the tar file. This is then used to + establish if the server has any newer data and hence if it is worth + downloading fresh data. The process uses the standard HTTP + If-Modified-Since header. +*/ + +class InfoJsonExtractEntryListener : public TarEntryListener +{ +public: + InfoJsonExtractEntryListener(); + virtual ~InfoJsonExtractEntryListener(); + + virtual status_t Handle( + const TarArchiveHeader& header, + size_t offset, + BDataIO *data); + + BPositionIO& GetInfoJsonData(); +private: + BMallocIO fInfoJsonData; +}; + + +InfoJsonExtractEntryListener::InfoJsonExtractEntryListener() +{ +} + +InfoJsonExtractEntryListener::~InfoJsonExtractEntryListener() +{ +} + + +BPositionIO& +InfoJsonExtractEntryListener::GetInfoJsonData() +{ + return fInfoJsonData; +} + + +status_t +InfoJsonExtractEntryListener::Handle( const TarArchiveHeader& header, + size_t offset, BDataIO *data) +{ + if (header.Length() > 0 && header.FileName() == ENTRY_PATH_METADATA) { + status_t copyResult = DataIOUtils::CopyAll(&fInfoJsonData, data); + if (copyResult == B_OK) { + HDINFO("[InfoJsonExtractEntryListener] did extract [%s]", + ENTRY_PATH_METADATA); + fInfoJsonData.Seek(0, SEEK_SET); + return B_CANCELED; + // this will prevent further scanning of the tar file + } + return copyResult; + } + + return B_OK; +} + + +/*! This constructor will locate the cached data in a standardized location +*/ ServerIconExportUpdateProcess::ServerIconExportUpdateProcess( Model* model, uint32 serverProcessOptions) : - AbstractServerProcess(serverProcessOptions), - fModel(model), - fCountIconsSet(0) + AbstractSingleFileServerProcess(serverProcessOptions), + fModel(model) { - AutoLocker locker(fModel->Lock()); - if (fModel->IconStoragePath(fLocalIconStoragePath) != B_OK) { - HDINFO("[%s] unable to obtain the path for storing icons", Name()); - fLocalIconStoragePath.Unset(); - fLocalIconStore = NULL; - } else { - fLocalIconStore = new LocalIconStore(fLocalIconStoragePath); - } } ServerIconExportUpdateProcess::~ServerIconExportUpdateProcess() { - delete fLocalIconStore; } @@ -69,250 +120,100 @@ ServerIconExportUpdateProcess::Description() const status_t -ServerIconExportUpdateProcess::RunInternal() +ServerIconExportUpdateProcess::ProcessLocalData() { - status_t result = B_OK; + status_t result = fModel->InitPackageIconRepository(); + _NotifyPackagesWithIconsInDepots(); + return result; +} - if (NULL == fLocalIconStore || fLocalIconStoragePath.Path() == NULL) - result = B_ERROR; - if (IsSuccess(result) && HasOption(SERVER_PROCESS_DROP_CACHE)) { - result = StorageUtils::RemoveDirectoryContents(fLocalIconStoragePath); - } +status_t +ServerIconExportUpdateProcess::GetLocalPath(BPath& path) const +{ + return fModel->IconTarPath(path); +} + + +/*! This overridden method implementation seems inefficient because it will + apparently scan the entire tarball for the file that it is looking for, but + actually it will cancel the scan once it has found it's target object (the + JSON data containing the meta-data) and by convention, the server side will + place the meta-data as one of the first objects in the tar-ball so it will + find it quickly. +*/ + +status_t +ServerIconExportUpdateProcess::IfModifiedSinceHeaderValue(BString& headerValue) const +{ + headerValue.SetTo(""); + + BPath tarPath; + status_t result = GetLocalPath(tarPath); + + // early exit if the tar file is not there. if (result == B_OK) { - bool hasData; + off_t size; + bool hasFile; - result = HasLocalData(&hasData); + result = StorageUtils::ExistsObject(tarPath, &hasFile, NULL, &size); - if (result == B_OK && ShouldAttemptNetworkDownload(hasData)) - result = _DownloadAndUnpack(); - - if (IsSuccess(result)) { - status_t hasDataResult = HasLocalData(&hasData); - - if (hasDataResult == B_OK && !hasData) - result = HD_ERR_NO_DATA; - } - } - - if (IsSuccess(result) && !WasStopped()) - result = Populate(); - - return result; -} - - -status_t -ServerIconExportUpdateProcess::Populate() -{ - BStopWatch watch("ServerIconExportUpdateProcess::Populate", true); - DepotList depots = fModel->Depots(); - status_t result = B_OK; - - { - AutoLocker locker(fModel->Lock()); - depots = fModel->Depots(); - } - - HDDEBUG("[%s] will populate icons for %" B_PRId32 " depots", Name(), - depots.CountItems()); - - for (int32 i = 0; - (i < depots.CountItems()) && !WasStopped() && (result == B_OK); - i++) { - AutoLocker locker(fModel->Lock()); - DepotInfo depotInfo = depots.ItemAtFast(i); - result = PopulateForDepot(depotInfo); - } - - if (Logger::IsInfoEnabled()) { - double secs = watch.ElapsedTime() / 1000000.0; - HDINFO("[%s] did populate %" B_PRId32 " packages' icons (%6.3g secs)", - Name(), fCountIconsSet, secs); - } - - return result; -} - - -/*! This method assumes that the model lock has been acquired */ - -status_t -ServerIconExportUpdateProcess::PopulateForDepot(const DepotInfo& depot) -{ - HDINFO("[%s] will populate icons for depot [%s]", - Name(), depot.Name().String()); - status_t result = B_OK; - const PackageList& packages = depot.Packages(); - for(int32 j = 0; - (j < packages.CountItems()) && !WasStopped() && (result == B_OK); - j++) { - const PackageInfoRef& packageInfoRef = packages.ItemAtFast(j); - result = PopulateForPkg(packageInfoRef); - - if (result == B_FILE_NOT_FOUND) - result = B_OK; - } - - return result; -} - - -/*! This method assumes that the model lock has been acquired */ - -status_t -ServerIconExportUpdateProcess::PopulateForPkg(const PackageInfoRef& package) -{ - BPath bestIconPath; - - if ( fLocalIconStore->TryFindIconPath( - package->Name(), bestIconPath) == B_OK) { - - BFile bestIconFile(bestIconPath.Path(), O_RDONLY); - BitmapRef bitmapRef(new(std::nothrow)SharedBitmap(bestIconFile), true); - package->SetIcon(bitmapRef); - - HDDEBUG("[%s] have set the package icon for [%s] from [%s]", - Name(), package->Name().String(), bestIconPath.Path()); - - fCountIconsSet++; - - return B_OK; - } - - HDDEBUG("[%s] did not set the package icon for [%s]; no data", - Name(), package->Name().String()); - - return B_FILE_NOT_FOUND; -} - - -status_t -ServerIconExportUpdateProcess::_DownloadAndUnpack() -{ - BPath tarGzFilePath(tmpnam(NULL)); - status_t result = B_OK; - - HDINFO("[%s] will start fetching icons", Name()); - - result = _Download(tarGzFilePath); - - switch (result) { - case HD_ERR_NOT_MODIFIED: - HDINFO("[%s] icons not modified - will use existing", Name()); + if (result == B_OK && (!hasFile || size == 0)) return result; - case B_OK: - return _Unpack(tarGzFilePath); - default: - return (_HandleDownloadFailure() != B_OK) ? result : B_OK; - } -} - - -/*! if the download failed, but there are existing icons in place to use - then use those icons. To detect the existing files, look for the - icons' meta-info file. -*/ - -status_t -ServerIconExportUpdateProcess::_HandleDownloadFailure() -{ - bool hasData; - status_t result = HasLocalData(&hasData); - - if (result == B_OK) { - if (hasData) { - HDINFO("[%s] failed to update data, but have old data anyway " - "so will carry on with that", Name()); - } else { - HDINFO("[%s] failed to obtain data", Name()); - result = HD_ERR_NO_DATA; - } - } else { - HDERROR("[%s] unable to detect if there is local data\n", Name()); } - return result; -} - - -/*! The tar-ball data of icons has arrived and so old data needs to be purged - to make way for the new data and the new data needs to be unpacked. -*/ - -status_t -ServerIconExportUpdateProcess::_Unpack(BPath& tarGzFilePath) -{ - status_t result; - HDINFO("[%s] delete any existing stored data", Name()); - StorageUtils::RemoveDirectoryContents(fLocalIconStoragePath); - - BFile *tarGzFile = new BFile(tarGzFilePath.Path(), O_RDONLY); - BDataIO* tarIn; - - BZlibDecompressionParameters* zlibDecompressionParameters - = new BZlibDecompressionParameters(); - - result = BZlibCompressionAlgorithm() - .CreateDecompressingInputStream(tarGzFile, - zlibDecompressionParameters, tarIn); - if (result == B_OK) { - BStopWatch watch( - "ServerIconExportUpdateProcess::DownloadAndUnpack_Unpack", - true); + BFile *tarIo = new BFile(tarPath.Path(), O_RDONLY); + ObjectDeleter tarIoDeleter(tarIo); + InfoJsonExtractEntryListener* extractDataListener + = new InfoJsonExtractEntryListener(); + ObjectDeleter extractDataListenerDeleter( + extractDataListener); + result = TarArchiveService::ForEachEntry(*tarIo, extractDataListener); - result = TarArchiveService::Unpack(*tarIn, - fLocalIconStoragePath, NULL); + if (result == B_CANCELED) { + // the cancellation is expected because it will cancel when it finds + // the meta-data file to avoid any further cost of traversing the + // tar-ball. + result = B_OK; - if (result == B_OK) { - double secs = watch.ElapsedTime() / 1000000.0; - HDINFO("[%s] did unpack icon tgz in (%6.3g secs)", Name(), secs); + StandardMetaData metaData; + BString metaDataJsonPath; + GetStandardMetaDataJsonPath(metaDataJsonPath); + StandardMetaDataJsonEventListener parseInfoJsonListener( + metaDataJsonPath, metaData); + BPrivate::BJson::Parse( + &(extractDataListener->GetInfoJsonData()), + &parseInfoJsonListener); - if (0 != remove(tarGzFilePath.Path())) { - HDERROR("[%s] unable to delete the temporary tgz path; %s", - Name(), tarGzFilePath.Path()); + result = parseInfoJsonListener.ErrorStatus(); + + // An example of this output would be; 'Fri, 24 Oct 2014 19:32:27 +0000' + + if (result == B_OK) { + SetIfModifiedSinceHeaderValueFromMetaData( + headerValue, metaData); + } else { + HDERROR("[%s] unable to parse the meta data from the tar file", + Name()); } + } else { + HDERROR("[%s] did not find the metadata [%s] in the tar", + Name(), ENTRY_PATH_METADATA); + result = B_BAD_DATA; } } - delete tarGzFile; - HDINFO("[%s] did complete unpacking icons", Name()); return result; } -status_t -ServerIconExportUpdateProcess::HasLocalData(bool* result) const -{ - BPath path; - status_t status = GetStandardMetaDataPath(path); - - if (status != B_OK) - return status; - - off_t size; - - status = StorageUtils::ExistsObject(path, result, NULL, &size); - - if (status == B_OK && size == 0) - *result = false; - - return status; -} - - status_t ServerIconExportUpdateProcess::GetStandardMetaDataPath(BPath& path) const { - status_t result = fModel->IconStoragePath(path); - - if (result != B_OK) - return result; - - path.Append("hicn/info.json"); - return B_OK; + return B_ERROR; + // unsupported } @@ -320,17 +221,40 @@ void ServerIconExportUpdateProcess::GetStandardMetaDataJsonPath( BString& jsonPath) const { - // the "$" here indicates that the data is at the top level. jsonPath.SetTo("$"); + // the "$" here indicates that the data is at the top level. } -status_t -ServerIconExportUpdateProcess::_Download(BPath& tarGzFilePath) +BString +ServerIconExportUpdateProcess::UrlPathComponent() { - return DownloadToLocalFileAtomically(tarGzFilePath, - ServerSettings::CreateFullUrl("/__pkgicon/all.tar.gz")); + return "/__pkgicon/all.tar.gz"; } +void +ServerIconExportUpdateProcess::_NotifyPackagesWithIconsInDepots() const +{ + const DepotList& depots = fModel->Depots(); + for (int32 d = 0; d < depots.CountItems(); d++) { + const DepotInfo& depot = depots.ItemAtFast(d); + _NotifyPackagesWithIconsInDepot(depot); + } +} + +void +ServerIconExportUpdateProcess::_NotifyPackagesWithIconsInDepot( + const DepotInfo& depot) const +{ + PackageIconRepository& packageIconRepository + = fModel->GetPackageIconRepository(); + const PackageList& packages = depot.Packages(); + for (int32 p = 0; p < packages.CountItems(); p++) { + AutoLocker locker(fModel->Lock()); + const PackageInfoRef& packageInfoRef = packages.ItemAtFast(p); + if (packageIconRepository.HasAnyIcon(packageInfoRef->Name())) + packages.ItemAtFast(p)->NotifyChangedIcon(); + } +} \ No newline at end of file diff --git a/src/apps/haikudepot/server/ServerIconExportUpdateProcess.h b/src/apps/haikudepot/server/ServerIconExportUpdateProcess.h index 2df940cd61..0ef1d1f6c5 100644 --- a/src/apps/haikudepot/server/ServerIconExportUpdateProcess.h +++ b/src/apps/haikudepot/server/ServerIconExportUpdateProcess.h @@ -1,5 +1,5 @@ /* - * Copyright 2017-2018, Andrew Lindesay . + * Copyright 2017-2020, Andrew Lindesay . * All rights reserved. Distributed under the terms of the MIT License. */ #ifndef SERVER_ICON_EXPORT_UPDATE_PROCESS_H @@ -11,15 +11,14 @@ #include #include -#include "AbstractServerProcess.h" -#include "LocalIconStore.h" +#include "AbstractSingleFileServerProcess.h" #include "Model.h" class DumpExportPkg; -class ServerIconExportUpdateProcess : public AbstractServerProcess { +class ServerIconExportUpdateProcess : public AbstractSingleFileServerProcess { public: ServerIconExportUpdateProcess( @@ -29,26 +28,27 @@ public: const char* Name() const; const char* Description() const; - status_t RunInternal(); + virtual status_t ProcessLocalData(); + + virtual status_t GetLocalPath(BPath& path) const; + virtual status_t IfModifiedSinceHeaderValue( + BString& headerValue) const; + + + virtual status_t GetStandardMetaDataPath(BPath& path) const; + virtual void GetStandardMetaDataJsonPath( + BString& jsonPath) const; protected: - status_t PopulateForPkg(const PackageInfoRef& package); - status_t PopulateForDepot(const DepotInfo& depot); - status_t Populate(); - status_t HasLocalData(bool* result) const; - status_t GetStandardMetaDataPath(BPath& path) const; - void GetStandardMetaDataJsonPath( - BString& jsonPath) const; -private: - status_t _Unpack(BPath& tarGzFilePath); - status_t _HandleDownloadFailure(); - status_t _DownloadAndUnpack(); - status_t _Download(BPath& tarGzFilePath); + virtual BString UrlPathComponent(); +private: + void _NotifyPackagesWithIconsInDepots() const; + void _NotifyPackagesWithIconsInDepot( + const DepotInfo& depotInfo) const; + +private: Model* fModel; - BPath fLocalIconStoragePath; - LocalIconStore* fLocalIconStore; - int32 fCountIconsSet; }; diff --git a/src/apps/haikudepot/server/StandardMetaData.cpp b/src/apps/haikudepot/server/StandardMetaData.cpp index 19505f4561..afb319bf91 100644 --- a/src/apps/haikudepot/server/StandardMetaData.cpp +++ b/src/apps/haikudepot/server/StandardMetaData.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2017, Andrew Lindesay . + * Copyright 2017-2020, Andrew Lindesay . * All rights reserved. Distributed under the terms of the MIT License. */ @@ -14,7 +14,7 @@ StandardMetaData::StandardMetaData() } -BDateTime +/*static*/ BDateTime StandardMetaData::_CreateDateTime(uint64_t millisSinceEpoc) { time_t secondsSinceEpoc = (millisSinceEpoc / 1000); @@ -25,14 +25,14 @@ StandardMetaData::_CreateDateTime(uint64_t millisSinceEpoc) uint64_t -StandardMetaData::GetCreateTimestamp() +StandardMetaData::GetCreateTimestamp() const { return fCreateTimestamp; } BDateTime -StandardMetaData::GetCreateTimestampAsDateTime() +StandardMetaData::GetCreateTimestampAsDateTime() const { return _CreateDateTime(GetCreateTimestamp()); } @@ -46,7 +46,7 @@ StandardMetaData::SetCreateTimestamp(uint64_t value) uint64_t -StandardMetaData::GetDataModifiedTimestamp() +StandardMetaData::GetDataModifiedTimestamp() const { return fDataModifiedTimestamp; } @@ -60,14 +60,14 @@ StandardMetaData::SetDataModifiedTimestamp(uint64_t value) BDateTime -StandardMetaData::GetDataModifiedTimestampAsDateTime() +StandardMetaData::GetDataModifiedTimestampAsDateTime() const { return _CreateDateTime(GetDataModifiedTimestamp()); } bool -StandardMetaData::IsPopulated() +StandardMetaData::IsPopulated() const { return fCreateTimestamp != 0 && fDataModifiedTimestamp != 0; } diff --git a/src/apps/haikudepot/server/StandardMetaData.h b/src/apps/haikudepot/server/StandardMetaData.h index 0b1ed9057b..08b8fbce28 100644 --- a/src/apps/haikudepot/server/StandardMetaData.h +++ b/src/apps/haikudepot/server/StandardMetaData.h @@ -1,5 +1,5 @@ /* - * Copyright 2017, Andrew Lindesay . + * Copyright 2017-2020, Andrew Lindesay . * All rights reserved. Distributed under the terms of the MIT License. */ #ifndef STANDARD_META_DATA_H @@ -23,19 +23,22 @@ class StandardMetaData { public: StandardMetaData(); - uint64_t GetCreateTimestamp(); - BDateTime GetCreateTimestampAsDateTime(); + uint64_t GetCreateTimestamp() const; + BDateTime GetCreateTimestampAsDateTime() const; void SetCreateTimestamp(uint64_t value); - uint64_t GetDataModifiedTimestamp(); - BDateTime GetDataModifiedTimestampAsDateTime(); + uint64_t GetDataModifiedTimestamp() const; + BDateTime GetDataModifiedTimestampAsDateTime() + const; void SetDataModifiedTimestamp( uint64_t value); - bool IsPopulated(); + bool IsPopulated() const; private: - BDateTime _CreateDateTime( + static BDateTime _CreateDateTime( uint64_t millisSinceEpoc); + +private: uint64_t fCreateTimestamp; uint64_t fDataModifiedTimestamp; }; diff --git a/src/apps/haikudepot/tar/TarArchiveHeader.cpp b/src/apps/haikudepot/tar/TarArchiveHeader.cpp index 387cc7bc53..cccd1993ae 100644 --- a/src/apps/haikudepot/tar/TarArchiveHeader.cpp +++ b/src/apps/haikudepot/tar/TarArchiveHeader.cpp @@ -5,144 +5,57 @@ #include "TarArchiveHeader.h" -#include "Logger.h" -#define OFFSET_FILENAME 0 -#define OFFSET_LENGTH 124 -#define OFFSET_CHECKSUM 148 -#define OFFSET_FILETYPE 156 - -#define LENGTH_FILENAME 100 -#define LENGTH_LENGTH 12 -#define LENGTH_CHECKSUM 8 -#define LENGTH_BLOCK 512 - - -TarArchiveHeader::TarArchiveHeader(const BString& fileName, uint64 length, - tar_file_type fileType) - : - fFileName(fileName), - fLength(length), - fFileType(fileType) +TarArchiveHeader::TarArchiveHeader() + : + fFileName(), + fLength(0), + fFileType(TAR_FILE_TYPE_NORMAL) { } -tar_file_type -TarArchiveHeader::_ReadFileType(unsigned char data) { - switch (data) { - case 0: - case '0': - return TAR_FILE_TYPE_NORMAL; - default: - return TAR_FILE_TYPE_OTHER; - } -} - - -const BString -TarArchiveHeader::_ReadString(const unsigned char *data, size_t dataLength) +TarArchiveHeader::~TarArchiveHeader() { - uint32 actualLength = 0; - - while (actualLength < dataLength && 0 != data[actualLength]) - actualLength++; - - return BString((const char *) data, actualLength); } -/* - * This is an octal value represented as an ASCII string. - */ - -static bool tar_is_octal_digit(unsigned char c) -{ - switch (c) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - return true; - - default: - return false; - } -} - -uint32 -TarArchiveHeader::_ReadNumeric(const unsigned char *data, size_t dataLength) -{ - uint32 actualLength = 0; - - while (actualLength < dataLength && tar_is_octal_digit(data[actualLength])) - actualLength++; - - uint32 factor = 1; - uint32 result = 0; - - for (uint32 i = 0; i < actualLength; i++) { - result += (data[actualLength - (1 + i)] - '0') * factor; - factor *= 8; - } - - return result; -} - -uint32 -TarArchiveHeader::_CalculateChecksum(const unsigned char* block) -{ - uint32 result = 0; - - for (uint32 i = 0; i < LENGTH_BLOCK; i++) { - if (i >= OFFSET_CHECKSUM && i < OFFSET_CHECKSUM + LENGTH_CHECKSUM) - result += 32; - else - result += block[i]; - } - - return result; -} - -TarArchiveHeader* -TarArchiveHeader::CreateFromBlock(const unsigned char* block) -{ - uint32 actualChecksum = _CalculateChecksum(block); - uint32 expectedChecksum = _ReadNumeric(&block[OFFSET_CHECKSUM], - LENGTH_CHECKSUM); - - if(actualChecksum != expectedChecksum) { - HDERROR("tar archive header has bad checksum;" - "expected %" B_PRIu32 " actual %" B_PRIu32, - expectedChecksum, actualChecksum); - } else { - return new TarArchiveHeader( - _ReadString(&block[OFFSET_FILENAME], LENGTH_FILENAME), - _ReadNumeric(&block[OFFSET_LENGTH], LENGTH_LENGTH), - _ReadFileType(block[OFFSET_FILETYPE])); - } - - return NULL; -} const BString& -TarArchiveHeader::GetFileName() const +TarArchiveHeader::FileName() const { return fFileName; } size_t -TarArchiveHeader::GetLength() const +TarArchiveHeader::Length() const { return fLength; } tar_file_type -TarArchiveHeader::GetFileType() const +TarArchiveHeader::FileType() const { return fFileType; } + + +void +TarArchiveHeader::SetFileName(const BString& value) +{ + fFileName = value; +} + + +void +TarArchiveHeader::SetLength(size_t value) +{ + fLength = value; +} + + +void +TarArchiveHeader::SetFileType(tar_file_type value) +{ + fFileType = value; +} \ No newline at end of file diff --git a/src/apps/haikudepot/tar/TarArchiveHeader.h b/src/apps/haikudepot/tar/TarArchiveHeader.h index 9c92f545ab..1ece9c18f2 100644 --- a/src/apps/haikudepot/tar/TarArchiveHeader.h +++ b/src/apps/haikudepot/tar/TarArchiveHeader.h @@ -1,8 +1,7 @@ /* - * Copyright 2017, Andrew Lindesay . + * Copyright 2017-2020, Andrew Lindesay . * All rights reserved. Distributed under the terms of the MIT License. */ - #ifndef TAR_ARCHIVE_HEADER_H #define TAR_ARCHIVE_HEADER_H @@ -21,28 +20,20 @@ enum tar_file_type { class TarArchiveHeader { public: - TarArchiveHeader(const BString& fileName, - uint64 length, tar_file_type fileType); + TarArchiveHeader(); + virtual ~TarArchiveHeader(); - static TarArchiveHeader* CreateFromBlock(const unsigned char *block); - - const BString& GetFileName() const; - size_t GetLength() const; - tar_file_type GetFileType() const; + const BString& FileName() const; + size_t Length() const; + tar_file_type FileType() const; + void SetFileName(const BString& value); + void SetLength(size_t value); + void SetFileType(tar_file_type value); private: - static uint32 _CalculateChecksum( - const unsigned char* data); - static const BString _ReadString(const unsigned char* data, - size_t dataLength); - static uint32 _ReadNumeric(const unsigned char* data, - size_t dataLength); - static tar_file_type _ReadFileType(unsigned char data); - - const BString fFileName; + BString fFileName; uint64 fLength; tar_file_type fFileType; - }; #endif // TAR_ARCHIVE_HEADER_H diff --git a/src/apps/haikudepot/tar/TarArchiveService.cpp b/src/apps/haikudepot/tar/TarArchiveService.cpp index 42d2654806..c0017b3236 100644 --- a/src/apps/haikudepot/tar/TarArchiveService.cpp +++ b/src/apps/haikudepot/tar/TarArchiveService.cpp @@ -2,186 +2,217 @@ * Copyright 2017-2020, Andrew Lindesay . * All rights reserved. Distributed under the terms of the MIT License. */ - - #include "TarArchiveService.h" +#include #include #include #include +#include "DataIOUtils.h" #include "Logger.h" #include "StorageUtils.h" +#define OFFSET_FILENAME 0 +#define OFFSET_LENGTH 124 +#define OFFSET_CHECKSUM 148 +#define OFFSET_FILETYPE 156 + +#define LENGTH_FILENAME 100 +#define LENGTH_LENGTH 12 +#define LENGTH_CHECKSUM 8 #define LENGTH_BLOCK 512 -status_t -TarArchiveService::Unpack(BDataIO& tarDataIo, BPath& targetDirectory, - Stoppable* stoppable) +/*! This method will parse the header that is located at the current position of + the supplied tarIo. Upon success, the tarIo will point to the start of the + data for the parsed entry. +*/ + +/*static*/ status_t +TarArchiveService::GetEntry(BPositionIO& tarIo, TarArchiveHeader& header) +{ + uint8 buffer[LENGTH_BLOCK]; + status_t result = tarIo.ReadExactly(buffer, LENGTH_BLOCK); + if (result == B_OK) + result = _ReadHeader(buffer, header); + return result; +} + + +/*! For each entry in the tar file, this method will call the listener. If the + listener does not return B_OK then the process will stop. This method is + useful for obtaining a catalog of items in the tar file for example. +*/ + +/*status*/ status_t +TarArchiveService::ForEachEntry(BPositionIO& tarIo, TarEntryListener* listener) { uint8 buffer[LENGTH_BLOCK]; uint8 zero_buffer[LENGTH_BLOCK]; status_t result = B_OK; - uint32_t count_items_read = 0; - - HDINFO("will unpack to [%s]", targetDirectory.Path()); + uint32_t countItemsRead = 0; + off_t offset = 0; memset(zero_buffer, 0, sizeof zero_buffer); + tarIo.Seek(offset, SEEK_SET); - while (B_OK == result - && (NULL == stoppable || !stoppable->WasStopped()) - && B_OK == (result = tarDataIo.ReadExactly(buffer, LENGTH_BLOCK))) { + while (result == B_OK + && B_OK == (result = tarIo.ReadExactly( + buffer, LENGTH_BLOCK))) { - count_items_read++; - - if (0 == memcmp(zero_buffer, buffer, sizeof zero_buffer)) { - HDDEBUG("detected end of tar-ball"); + if (memcmp(zero_buffer, buffer, sizeof zero_buffer) == 0) { + HDDEBUG("detected end of tar-ball after %" B_PRIu32 " items", + countItemsRead); return B_OK; // end of tar-ball. - } else { - TarArchiveHeader* header = TarArchiveHeader::CreateFromBlock( - buffer); - - if (NULL == header) { - HDERROR("unable to parse a tar header"); - result = B_ERROR; - } - - if (B_OK == result) - result = _UnpackItem(tarDataIo, targetDirectory, *header); - - delete header; } - } - HDERROR("did unpack %d tar items", count_items_read); + TarArchiveHeader header; + result = _ReadHeader(buffer, header); - if (B_OK != result) { - HDERROR("error occurred unpacking tar items; %s", strerror(result)); - } - - return result; -} - - -status_t -TarArchiveService::_EnsurePathToTarItemFile( - BPath& targetDirectoryPath, BString &tarItemPath) -{ - if (tarItemPath.Length() == 0) - return B_ERROR; - - BStringList components; - tarItemPath.Split("/", false, components); - - for (int32 i = 0; i < components.CountStrings(); i++) { - BString component = components.StringAt(i); - - if (_ValidatePathComponent(component) != B_OK) { - HDERROR("malformed component; [%s]", component.String()); - return B_ERROR; - } - } - - BPath parentPath; - BPath assembledPath(targetDirectoryPath); - - status_t result = assembledPath.Append(tarItemPath); - - if (result == B_OK) - result = assembledPath.GetParent(&parentPath); - - if (result == B_OK) - result = create_directory(parentPath.Path(), 0777); - - return result; -} - - -status_t -TarArchiveService::_UnpackItem(BDataIO& tarDataIo, - BPath& targetDirectoryPath, - TarArchiveHeader& header) -{ - status_t result = B_OK; - BString entryFileName = header.GetFileName(); - uint32 entryLength = header.GetLength(); - - HDDEBUG("will unpack item [%s] length [%" B_PRIu32 "]b", - entryFileName.String(), entryLength); - - // if the path ends in "/" then it is a directory and there's no need to - // unpack it although if there is a length, it will need to be skipped. - - if (!entryFileName.EndsWith("/") || - header.GetFileType() != TAR_FILE_TYPE_NORMAL) { - - result = _EnsurePathToTarItemFile(targetDirectoryPath, - entryFileName); + HDTRACE("did read tar entry header for [%s]", + header.FileName().String()); if (result == B_OK) { - BPath targetFilePath(targetDirectoryPath); - targetFilePath.Append(entryFileName, false); - result = _UnpackItemData(tarDataIo, targetFilePath, entryLength); + countItemsRead++; + + // call out to the listener to read the data from the entry + // and/or just process the header information. + + if (listener != NULL) { + BDataIO *entryData = new ConstraintedDataIO(&tarIo, + header.Length()); + ObjectDeleter entryDataDeleter(entryData); + result = listener->Handle(header, offset, entryData); + } } - } else { - off_t blocksToSkip = (entryLength / LENGTH_BLOCK); - if (entryLength % LENGTH_BLOCK > 0) - blocksToSkip += 1; + offset += LENGTH_BLOCK; + offset += _BytesRoundedToBlocks(header.Length()); - if (0 != blocksToSkip) { - uint8 buffer[LENGTH_BLOCK]; + if (result == B_OK) + tarIo.Seek(offset, SEEK_SET); + } - for (uint32 i = 0; B_OK == result && i < blocksToSkip; i++) - tarDataIo.ReadExactly(buffer, LENGTH_BLOCK); - } + if (result == B_OK || result == B_CANCELED) + HDINFO("did list %" B_PRIu32 " tar items", countItemsRead); + else + HDERROR("error occurred listing tar items; %s", strerror(result)); + + return result; +} + + +tar_file_type +TarArchiveService::_ReadHeaderFileType(unsigned char data) { + switch (data) { + case 0: + case '0': + return TAR_FILE_TYPE_NORMAL; + default: + return TAR_FILE_TYPE_OTHER; + } +} + + +/*const*/ const BString +TarArchiveService::_ReadHeaderString(const uint8 *data, size_t dataLength) +{ + uint32 actualLength = 0; + while (actualLength < dataLength && 0 != data[actualLength]) + actualLength++; + return BString((const char *) data, actualLength); +} + + +/*! This function will return true if the character supplied is a valid + character in an number expressed in octal. +*/ + +static bool tar_is_octal_digit(unsigned char c) +{ + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + return true; + default: + return false; + } +} + + +/*static*/ uint32 +TarArchiveService::_ReadHeaderNumeric(const uint8 *data, size_t dataLength) +{ + uint32 actualLength = 0; + + while (actualLength < dataLength && tar_is_octal_digit(data[actualLength])) + actualLength++; + + uint32 factor = 1; + uint32 result = 0; + + for (uint32 i = 0; i < actualLength; i++) { + result += (data[actualLength - (1 + i)] - '0') * factor; + factor *= 8; } return result; } -status_t -TarArchiveService::_UnpackItemData(BDataIO& tarDataIo, - BPath& targetFilePath, uint32 length) +/*static*/ uint32 +TarArchiveService::_CalculateBlockChecksum(const uint8* block) { - uint8 buffer[LENGTH_BLOCK]; - size_t remainingInItem = length; - status_t result = B_OK; - BFile targetFile(targetFilePath.Path(), O_WRONLY | O_CREAT); + uint32 result = 0; - while (remainingInItem > 0 && - B_OK == result && - B_OK == (result = tarDataIo.ReadExactly(buffer, LENGTH_BLOCK))) { - - size_t writeFromBuffer = LENGTH_BLOCK; - - if (remainingInItem < LENGTH_BLOCK) - writeFromBuffer = remainingInItem; - - result = targetFile.WriteExactly(buffer, writeFromBuffer); - remainingInItem -= writeFromBuffer; - } - - if (result != B_OK) { - HDERROR("unable to unpack item data to; [%s]", targetFilePath.Path()); + for (uint32 i = 0; i < LENGTH_BLOCK; i++) { + if (i >= OFFSET_CHECKSUM && i < OFFSET_CHECKSUM + LENGTH_CHECKSUM) + result += 32; + else + result += (uint32) block[i]; } return result; } -status_t -TarArchiveService::_ValidatePathComponent(const BString& component) +/*static*/ status_t +TarArchiveService::_ReadHeader(const uint8* block, TarArchiveHeader& header) { - if (component.Length() == 0) - return B_ERROR; + uint32 actualChecksum = _CalculateBlockChecksum(block); + uint32 expectedChecksum = _ReadHeaderNumeric(&block[OFFSET_CHECKSUM], + LENGTH_CHECKSUM); - if (component == ".." || component == "." || component == "~") - return B_ERROR; + if(actualChecksum != expectedChecksum) { + HDERROR("tar archive header has bad checksum;" + "expected %" B_PRIu32 " actual %" B_PRIu32, + expectedChecksum, actualChecksum); + return B_BAD_DATA; + } + + header.SetFileName( + _ReadHeaderString(&block[OFFSET_FILENAME], LENGTH_FILENAME)); + header.SetLength( + _ReadHeaderNumeric(&block[OFFSET_LENGTH], LENGTH_LENGTH)); + header.SetFileType( + _ReadHeaderFileType(block[OFFSET_FILETYPE])); return B_OK; } + +/*static*/ off_t +TarArchiveService::_BytesRoundedToBlocks(off_t value) +{ + if (0 != value % LENGTH_BLOCK) + return ((value / LENGTH_BLOCK) + 1) * LENGTH_BLOCK; + return (value / LENGTH_BLOCK) * LENGTH_BLOCK; +} diff --git a/src/apps/haikudepot/tar/TarArchiveService.h b/src/apps/haikudepot/tar/TarArchiveService.h index e6b3363a36..df0b92fc0c 100644 --- a/src/apps/haikudepot/tar/TarArchiveService.h +++ b/src/apps/haikudepot/tar/TarArchiveService.h @@ -1,5 +1,5 @@ /* - * Copyright 2017-2018, Andrew Lindesay . + * Copyright 2017-2020, Andrew Lindesay . * All rights reserved. Distributed under the terms of the MIT License. */ #ifndef TAR_ARCHIVE_SERVICE_H @@ -12,24 +12,37 @@ #include +class TarEntryListener { +public: + virtual status_t Handle( + const TarArchiveHeader& header, + size_t offset, + BDataIO* data) = 0; +}; + + class TarArchiveService { public: - static status_t Unpack(BDataIO& tarDataIo, - BPath& targetDirectoryPath, - Stoppable* stoppable); + static status_t ForEachEntry(BPositionIO& tarIo, + TarEntryListener* listener); + static status_t GetEntry(BPositionIO& tarIo, + TarArchiveHeader& header); private: - static status_t _EnsurePathToTarItemFile( - BPath& targetDirectoryPath, - BString &tarItemPath); - static status_t _ValidatePathComponent( + static status_t _ValidatePathComponent( const BString& component); - static status_t _UnpackItem(BDataIO& tarDataIo, - BPath& targetDirectory, - TarArchiveHeader& header); - static status_t _UnpackItemData(BDataIO& tarDataIo, - BPath& targetFilePath, - uint32 length); + + static off_t _BytesRoundedToBlocks(off_t value); + static uint32 _CalculateBlockChecksum( + const unsigned char* data); + + static status_t _ReadHeader(const uint8* data, + TarArchiveHeader& header); + static const BString _ReadHeaderString(const uint8* data, + size_t dataLength); + static uint32 _ReadHeaderNumeric(const uint8* data, + size_t dataLength); + static tar_file_type _ReadHeaderFileType(uint8 data); }; diff --git a/src/apps/haikudepot/ui/App.cpp b/src/apps/haikudepot/ui/App.cpp index 649e89f1f5..35e2080232 100644 --- a/src/apps/haikudepot/ui/App.cpp +++ b/src/apps/haikudepot/ui/App.cpp @@ -26,6 +26,7 @@ #include "FeaturedPackagesView.h" #include "Logger.h" #include "MainWindow.h" +#include "PackageIconTarRepository.h" #include "ServerHelper.h" #include "ServerSettings.h" #include "ScreenshotWindow.h" @@ -54,7 +55,7 @@ App::~App() // We cannot let global destructors cleanup static BitmapRef objects, // since calling BBitmap destructors needs a valid BApplication still // around. That's why we do it here. - PackageInfo::CleanupDefaultIcon(); + PackageIconTarRepository::CleanupDefaultIcon(); FeaturedPackagesView::CleanupIcons(); ScreenshotWindow::CleanupIcons(); } diff --git a/src/apps/haikudepot/ui/FeaturedPackagesView.cpp b/src/apps/haikudepot/ui/FeaturedPackagesView.cpp index 6db503d0f8..eed6702a4b 100644 --- a/src/apps/haikudepot/ui/FeaturedPackagesView.cpp +++ b/src/apps/haikudepot/ui/FeaturedPackagesView.cpp @@ -55,12 +55,13 @@ static BitmapRef sInstalledIcon(new(std::nothrow) class StackedFeaturedPackagesView : public BView { public: - StackedFeaturedPackagesView() + StackedFeaturedPackagesView(Model& model) : BView("stacked featured packages view", B_WILL_DRAW | B_FRAME_EVENTS), + fModel(model), + fSelectedIndex(-1), fPackageListener( - new(std::nothrow) OnePackageMessagePackageListener(this)), - fSelectedIndex(-1) + new(std::nothrow) OnePackageMessagePackageListener(this)) { SetEventMask(B_POINTER_EVENTS); Clear(); @@ -376,17 +377,20 @@ public: void _DrawPackageIcon(BRect updateRect, PackageInfoRef pkg, float y, bool selected) { - BitmapRef icon = pkg->Icon(); + BitmapRef icon; + status_t iconResult = fModel.GetPackageIconRepository().GetIcon( + pkg->Name(), BITMAP_SIZE_64, icon); - if (icon.Get() != NULL) { - float inset = (HEIGHT_PACKAGE - SIZE_ICON) / 2.0; - BRect sourceRect = BRect(0, 0, SIZE_ICON, SIZE_ICON); - BRect targetRect = BRect(inset, y + inset, SIZE_ICON + inset, - y + SIZE_ICON + inset); - const BBitmap* bitmap = icon->Bitmap(SharedBitmap::SIZE_64); - SetDrawingMode(B_OP_ALPHA); - DrawBitmap(bitmap, bitmap->Bounds(), targetRect, - B_FILTER_BITMAP_BILINEAR); + if (iconResult == B_OK) { + if (icon.Get() != NULL) { + float inset = (HEIGHT_PACKAGE - SIZE_ICON) / 2.0; + BRect targetRect = BRect(inset, y + inset, SIZE_ICON + inset, + y + SIZE_ICON + inset); + const BBitmap* bitmap = icon->Bitmap(BITMAP_SIZE_64); + SetDrawingMode(B_OP_ALPHA); + DrawBitmap(bitmap, bitmap->Bounds(), targetRect, + B_FILTER_BITMAP_BILINEAR); + } } } @@ -415,7 +419,7 @@ public: if (pkg->State() == ACTIVATED) { const BBitmap* bitmap = sInstalledIcon->Bitmap( - SharedBitmap::SIZE_16); + BITMAP_SIZE_16); float stringWidth = StringWidth(pkg->Title()); float offsetX = pt.x + stringWidth + PADDING; BRect targetRect(offsetX, pt.y - 16, offsetX + 16, pt.y); @@ -599,10 +603,10 @@ public: private: + Model& fModel; std::vector fPackages; int32 fSelectedIndex; - OnePackageMessagePackageListener* fPackageListener; }; @@ -611,11 +615,12 @@ private: // #pragma mark - FeaturedPackagesView -FeaturedPackagesView::FeaturedPackagesView() +FeaturedPackagesView::FeaturedPackagesView(Model& model) : - BView(B_TRANSLATE("Featured packages"), 0) + BView(B_TRANSLATE("Featured packages"), 0), + fModel(model) { - fPackagesView = new StackedFeaturedPackagesView(); + fPackagesView = new StackedFeaturedPackagesView(fModel); fScrollView = new BScrollView("featured packages scroll view", fPackagesView, 0, false, true, B_FANCY_BORDER); diff --git a/src/apps/haikudepot/ui/FeaturedPackagesView.h b/src/apps/haikudepot/ui/FeaturedPackagesView.h index a4abc60e67..ecc7ef19c3 100644 --- a/src/apps/haikudepot/ui/FeaturedPackagesView.h +++ b/src/apps/haikudepot/ui/FeaturedPackagesView.h @@ -9,6 +9,7 @@ #include +#include "Model.h" #include "PackageInfo.h" #include "PackageInfoListener.h" @@ -18,7 +19,7 @@ class StackedFeaturedPackagesView; class FeaturedPackagesView : public BView { public: - FeaturedPackagesView(); + FeaturedPackagesView(Model& model); virtual ~FeaturedPackagesView(); virtual void DoLayout(); @@ -33,10 +34,10 @@ public: static void CleanupIcons(); private: - void _AdjustViews(); + void _AdjustViews(); private: - + Model& fModel; BScrollView* fScrollView; StackedFeaturedPackagesView* fPackagesView; diff --git a/src/apps/haikudepot/ui/MainWindow.cpp b/src/apps/haikudepot/ui/MainWindow.cpp index a1988c04cd..f3a769af6b 100644 --- a/src/apps/haikudepot/ui/MainWindow.cpp +++ b/src/apps/haikudepot/ui/MainWindow.cpp @@ -153,9 +153,9 @@ MainWindow::MainWindow(const BMessage& settings) menuBar->MaxSize().height)); fFilterView = new FilterView(); - fFeaturedPackagesView = new FeaturedPackagesView(); - fPackageListView = new PackageListView(fModel.Lock()); - fPackageInfoView = new PackageInfoView(fModel.Lock(), this); + fFeaturedPackagesView = new FeaturedPackagesView(fModel); + fPackageListView = new PackageListView(&fModel); + fPackageInfoView = new PackageInfoView(&fModel, this); fSplitView = new BSplitView(B_VERTICAL, 5.0f); @@ -236,7 +236,7 @@ MainWindow::MainWindow(const BMessage& settings, const PackageInfoRef& package) debugger("unable to create the process coordinator semaphore"); fFilterView = new FilterView(); - fPackageInfoView = new PackageInfoView(fModel.Lock(), this); + fPackageInfoView = new PackageInfoView(&fModel, this); fWorkStatusView = new WorkStatusView("work status"); BLayoutBuilder::Group<>(this, B_VERTICAL) diff --git a/src/apps/haikudepot/ui/PackageInfoView.cpp b/src/apps/haikudepot/ui/PackageInfoView.cpp index 8623a23cbc..55d81829da 100644 --- a/src/apps/haikudepot/ui/PackageInfoView.cpp +++ b/src/apps/haikudepot/ui/PackageInfoView.cpp @@ -266,9 +266,10 @@ private: class TitleView : public BGroupView { public: - TitleView() + TitleView(PackageIconRepository& packageIconRepository) : - BGroupView("title view", B_HORIZONTAL) + BGroupView("title view", B_HORIZONTAL), + fPackageIconRepository(packageIconRepository) { fIconView = new BitmapView("package icon view"); fTitleView = new BStringView("package title view", ""); @@ -393,8 +394,12 @@ public: void SetPackage(const PackageInfo& package) { - if (package.Icon().Get() != NULL) - fIconView->SetBitmap(package.Icon(), SharedBitmap::SIZE_32); + BitmapRef bitmap; + status_t iconResult = fPackageIconRepository.GetIcon( + package.Name(), BITMAP_SIZE_64, bitmap); + + if (iconResult == B_OK) + fIconView->SetBitmap(bitmap, BITMAP_SIZE_32); else fIconView->UnsetBitmap(); @@ -447,6 +452,8 @@ public: } private: + PackageIconRepository& fPackageIconRepository; + BitmapView* fIconView; BStringView* fTitleView; @@ -811,9 +818,9 @@ public: fDescriptionView->SetText(package.ShortDescription(), package.FullDescription()); - fEmailIconView->SetBitmap(&fEmailIcon, SharedBitmap::SIZE_16); + fEmailIconView->SetBitmap(&fEmailIcon, BITMAP_SIZE_16); _SetContactInfo(fEmailLinkView, package.Publisher().Email()); - fWebsiteIconView->SetBitmap(&fWebsiteIcon, SharedBitmap::SIZE_16); + fWebsiteIconView->SetBitmap(&fWebsiteIcon, BITMAP_SIZE_16); _SetContactInfo(fWebsiteLinkView, package.Publisher().Website()); bool hasScreenshot = false; @@ -1281,11 +1288,11 @@ private: // #pragma mark - PackageInfoView -PackageInfoView::PackageInfoView(BLocker* modelLock, +PackageInfoView::PackageInfoView(Model* model, PackageActionHandler* handler) : BView("package info view", 0), - fModelLock(modelLock), + fModel(model), fPackageListener(new(std::nothrow) OnePackageMessagePackageListener(this)) { fCardLayout = new BCardLayout(); @@ -1310,7 +1317,7 @@ PackageInfoView::PackageInfoView(BLocker* modelLock, fCardLayout->SetVisibleItem((int32)0); - fTitleView = new TitleView(); + fTitleView = new TitleView(fModel->GetPackageIconRepository()); fPackageActionView = new PackageActionView(handler); fPackageActionView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); @@ -1364,7 +1371,7 @@ PackageInfoView::MessageReceived(BMessage* message) if (package->Name() != name) break; - BAutolock _(fModelLock); + BAutolock _(fModel->Lock()); if ((changes & PKG_CHANGED_SUMMARY) != 0 || (changes & PKG_CHANGED_DESCRIPTION) != 0 @@ -1396,7 +1403,7 @@ PackageInfoView::MessageReceived(BMessage* message) void PackageInfoView::SetPackage(const PackageInfoRef& packageRef) { - BAutolock _(fModelLock); + BAutolock _(fModel->Lock()); if (packageRef.Get() == NULL) { Clear(); @@ -1441,7 +1448,7 @@ PackageInfoView::SetPackage(const PackageInfoRef& packageRef) void PackageInfoView::Clear() { - BAutolock _(fModelLock); + BAutolock _(fModel->Lock()); fTitleView->Clear(); fPackageActionView->Clear(); diff --git a/src/apps/haikudepot/ui/PackageInfoView.h b/src/apps/haikudepot/ui/PackageInfoView.h index cd6c7b6e4e..7e8d7e4e1c 100644 --- a/src/apps/haikudepot/ui/PackageInfoView.h +++ b/src/apps/haikudepot/ui/PackageInfoView.h @@ -1,5 +1,6 @@ /* * Copyright 2013-2014, Stephan Aßmus . + * Copyright 2020, Andrew Lindesay * All rights reserved. Distributed under the terms of the MIT License. */ #ifndef PACKAGE_INFO_VIEW_H @@ -7,6 +8,7 @@ #include +#include "Model.h" #include "PackageInfo.h" #include "PackageInfoListener.h" @@ -27,7 +29,7 @@ enum { class PackageInfoView : public BView { public: - PackageInfoView(BLocker* modelLock, + PackageInfoView(Model* model, PackageActionHandler* handler); virtual ~PackageInfoView(); @@ -40,7 +42,7 @@ public: void Clear(); private: - BLocker* fModelLock; + Model* fModel; BCardLayout* fCardLayout; TitleView* fTitleView; diff --git a/src/apps/haikudepot/ui/PackageListView.cpp b/src/apps/haikudepot/ui/PackageListView.cpp index e0a2845db6..c63e882189 100644 --- a/src/apps/haikudepot/ui/PackageListView.cpp +++ b/src/apps/haikudepot/ui/PackageListView.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2018, Andrew Lindesay, . + * Copyright 2018-2020, Andrew Lindesay, . * Copyright 2017, Julian Harnath, . * Copyright 2015, Axel Dörfler, . * Copyright 2013-2014, Stephan Aßmus . @@ -63,24 +63,18 @@ package_state_to_string(PackageInfoRef ref) } -// A field type displaying both a bitmap and a string so that the -// tree display looks nicer (both text and bitmap are indented) -class SharedBitmapStringField : public BStringField { +class PackageIconAndTitleField : public BStringField { typedef BStringField Inherited; public: - SharedBitmapStringField(SharedBitmap* bitmap, - SharedBitmap::Size size, + PackageIconAndTitleField( + const char* packageName, const char* string); - virtual ~SharedBitmapStringField(); - - void SetBitmap(SharedBitmap* bitmap, - SharedBitmap::Size size); - const BBitmap* Bitmap() const - { return fBitmap; } + virtual ~PackageIconAndTitleField(); + const BString PackageName() const + { return fPackageName; } private: - BitmapRef fReference; - const BBitmap* fBitmap; + const BString fPackageName; }; @@ -111,12 +105,12 @@ private: // BColumn for PackageListView which knows how to render -// a SharedBitmapStringField -// TODO: Code-duplication with DriveSetup PartitionList.h +// a PackageIconAndTitleField class PackageColumn : public BTitledColumn { typedef BTitledColumn Inherited; public: - PackageColumn(const char* title, + PackageColumn(Model* model, + const char* title, float width, float minWidth, float maxWidth, uint32 truncateMode, alignment align = B_ALIGN_LEFT); @@ -132,6 +126,7 @@ public: static void InitTextMargin(BView* parent); private: + Model* fModel; uint32 fTruncateMode; static float sTextMargin; }; @@ -141,14 +136,15 @@ private: class PackageRow : public BRow { typedef BRow Inherited; public: - PackageRow(const PackageInfoRef& package, + PackageRow( + const PackageInfoRef& package, PackageListener* listener); virtual ~PackageRow(); const PackageInfoRef& Package() const { return fPackage; } - void UpdateTitle(); + void UpdateIconAndTitle(); void UpdateSummary(); void UpdateState(); void UpdateRating(); @@ -161,7 +157,8 @@ public: private: PackageInfoRef fPackage; - PackageInfoListenerRef fPackageListener; + PackageInfoListenerRef + fPackageListener; PackageRow* fNextInHash; // link for BOpenHashTable @@ -208,28 +205,17 @@ private: // #pragma mark - SharedBitmapStringField -SharedBitmapStringField::SharedBitmapStringField(SharedBitmap* bitmap, - SharedBitmap::Size size, const char* string) +PackageIconAndTitleField::PackageIconAndTitleField(const char* packageName, + const char* string) : Inherited(string), - fBitmap(NULL) -{ - SetBitmap(bitmap, size); -} - - -SharedBitmapStringField::~SharedBitmapStringField() + fPackageName(packageName) { } -void -SharedBitmapStringField::SetBitmap(SharedBitmap* bitmap, - SharedBitmap::Size size) +PackageIconAndTitleField::~PackageIconAndTitleField() { - fReference = bitmap; - fBitmap = bitmap != NULL ? bitmap->Bitmap(size) : NULL; - // TODO: cause a redraw? } @@ -312,10 +298,11 @@ SizeField::SetSize(double size) float PackageColumn::sTextMargin = 0.0; -PackageColumn::PackageColumn(const char* title, float width, float minWidth, - float maxWidth, uint32 truncateMode, alignment align) +PackageColumn::PackageColumn(Model* model, const char* title, float width, + float minWidth, float maxWidth, uint32 truncateMode, alignment align) : Inherited(title, width, minWidth, maxWidth, align), + fModel(model), fTruncateMode(truncateMode) { SetWantsEvents(true); @@ -325,14 +312,12 @@ PackageColumn::PackageColumn(const char* title, float width, float minWidth, void PackageColumn::DrawField(BField* field, BRect rect, BView* parent) { - SharedBitmapStringField* bitmapField - = dynamic_cast(field); + PackageIconAndTitleField* packageIconAndTitleField + = dynamic_cast(field); BStringField* stringField = dynamic_cast(field); RatingField* ratingField = dynamic_cast(field); - if (bitmapField != NULL) { - const BBitmap* bitmap = bitmapField->Bitmap(); - + if (packageIconAndTitleField != NULL) { // Scale the bitmap to 16x16 BRect r = BRect(0, 0, 15, 15); @@ -357,23 +342,33 @@ PackageColumn::DrawField(BField* field, BRect rect, BView* parent) break; } - if (width != bitmapField->Width()) { - BString truncatedString(bitmapField->String()); + if (width != packageIconAndTitleField->Width()) { + BString truncatedString(packageIconAndTitleField->String()); parent->TruncateString(&truncatedString, fTruncateMode, width + 2); - bitmapField->SetClippedString(truncatedString.String()); - bitmapField->SetWidth(width); + packageIconAndTitleField->SetClippedString(truncatedString.String()); + packageIconAndTitleField->SetWidth(width); } // draw the bitmap - if (bitmap != NULL) { - parent->SetDrawingMode(B_OP_ALPHA); - BRect viewRect(x, y, x + 15, y + 15); - parent->DrawBitmap(bitmap, bitmap->Bounds(), viewRect); - parent->SetDrawingMode(B_OP_OVER); + BitmapRef bitmapRef; + status_t bitmapResult; + + bitmapResult = fModel->GetPackageIconRepository().GetIcon( + packageIconAndTitleField->PackageName(), BITMAP_SIZE_16, + bitmapRef); + + if (bitmapResult == B_OK) { + if (bitmapRef.Get() != NULL) { + const BBitmap* bitmap = bitmapRef->Bitmap(BITMAP_SIZE_16); + parent->SetDrawingMode(B_OP_ALPHA); + BRect viewRect(x, y, x + 15, y + 15); + parent->DrawBitmap(bitmap, bitmap->Bounds(), viewRect); + parent->SetDrawingMode(B_OP_OVER); + } } // draw the string - DrawString(bitmapField->ClippedString(), parent, r); + DrawString(packageIconAndTitleField->ClippedString(), parent, r); } else if (stringField != NULL) { @@ -487,22 +482,20 @@ PackageColumn::CompareFields(BField* field1, BField* field2) float PackageColumn::GetPreferredWidth(BField *_field, BView* parent) const { - SharedBitmapStringField* bitmapField - = dynamic_cast(_field); + PackageIconAndTitleField* packageIconAndTitleField + = dynamic_cast(_field); BStringField* stringField = dynamic_cast(_field); float parentWidth = Inherited::GetPreferredWidth(_field, parent); float width = 0.0; - if (bitmapField) { - const BBitmap* bitmap = bitmapField->Bitmap(); + if (packageIconAndTitleField) { BFont font; parent->GetFont(&font); - width = font.StringWidth(bitmapField->String()) + 3 * sTextMargin; - if (bitmap) - width += bitmap->Bounds().Width(); - else - width += 16; + width = font.StringWidth(packageIconAndTitleField->String()) + + 3 * sTextMargin; + width += 16; + // for the icon; always 16px } else if (stringField) { BFont font; parent->GetFont(&font); @@ -558,7 +551,7 @@ PackageRow::PackageRow(const PackageInfoRef& packageRef, // Package icon and title // NOTE: The icon BBitmap is referenced by the fPackage member. - UpdateTitle(); + UpdateIconAndTitle(); // Rating UpdateRating(); @@ -590,13 +583,13 @@ PackageRow::~PackageRow() void -PackageRow::UpdateTitle() +PackageRow::UpdateIconAndTitle() { if (fPackage.Get() == NULL) return; - SetField(new SharedBitmapStringField(fPackage->Icon(), - SharedBitmap::SIZE_16, fPackage->Title()), kTitleColumn); + SetField(new PackageIconAndTitleField( + fPackage->Name(), fPackage->Title()), kTitleColumn); } @@ -785,10 +778,10 @@ struct PackageListView::RowByNameHashDefinition { // #pragma mark - PackageListView -PackageListView::PackageListView(BLocker* modelLock) +PackageListView::PackageListView(Model* model) : BColumnListView(B_TRANSLATE("All packages"), 0, B_FANCY_BORDER, true), - fModelLock(modelLock), + fModel(model), fPackageListener(new(std::nothrow) PackageListener(this)), fRowByNameTable(new RowByNameTable()), fWorkStatusView(NULL) @@ -796,29 +789,33 @@ PackageListView::PackageListView(BLocker* modelLock) float scale = be_plain_font->Size() / 12.f; float spacing = be_control_look->DefaultItemSpacing() * 2; - AddColumn(new PackageColumn(B_TRANSLATE("Name"), 150 * scale, 50 * scale, - 300 * scale, B_TRUNCATE_MIDDLE), kTitleColumn); - AddColumn(new PackageColumn(B_TRANSLATE("Rating"), 80 * scale, 50 * scale, - 100 * scale, B_TRUNCATE_MIDDLE), kRatingColumn); - AddColumn(new PackageColumn(B_TRANSLATE("Description"), 300 * scale, - 80 * scale, 1000 * scale, + AddColumn(new PackageColumn(fModel, B_TRANSLATE("Name"), + 150 * scale, 50 * scale, 300 * scale, + B_TRUNCATE_MIDDLE), kTitleColumn); + AddColumn(new PackageColumn(fModel, B_TRANSLATE("Rating"), + 80 * scale, 50 * scale, 100 * scale, + B_TRUNCATE_MIDDLE), kRatingColumn); + AddColumn(new PackageColumn(fModel, B_TRANSLATE("Description"), + 300 * scale, 80 * scale, 1000 * scale, B_TRUNCATE_MIDDLE), kDescriptionColumn); - PackageColumn* sizeColumn = new PackageColumn(B_TRANSLATE("Size"), + PackageColumn* sizeColumn = new PackageColumn(fModel, B_TRANSLATE("Size"), spacing + StringWidth(B_TRANSLATE("9999.99 KiB")), 50 * scale, 140 * scale, B_TRUNCATE_END); sizeColumn->SetAlignment(B_ALIGN_RIGHT); AddColumn(sizeColumn, kSizeColumn); - AddColumn(new PackageColumn(B_TRANSLATE("Status"), + AddColumn(new PackageColumn(fModel, B_TRANSLATE("Status"), spacing + StringWidth(B_TRANSLATE("Available")), 60 * scale, 140 * scale, B_TRUNCATE_END), kStatusColumn); - AddColumn(new PackageColumn(B_TRANSLATE("Repository"), 120 * scale, - 50 * scale, 200 * scale, B_TRUNCATE_MIDDLE), kRepositoryColumn); + AddColumn(new PackageColumn(fModel, B_TRANSLATE("Repository"), + 120 * scale, 50 * scale, 200 * scale, + B_TRUNCATE_MIDDLE), kRepositoryColumn); SetColumnVisible(kRepositoryColumn, false); // invisible by default - AddColumn(new PackageColumn(B_TRANSLATE("Version"), 50 * scale, - 50 * scale, 200 * scale, B_TRUNCATE_MIDDLE), kVersionColumn); + AddColumn(new PackageColumn(fModel, B_TRANSLATE("Version"), + 50 * scale, 50 * scale, 200 * scale, + B_TRUNCATE_MIDDLE), kVersionColumn); fItemCountView = new ItemCountView(); AddStatusView(fItemCountView); @@ -864,11 +861,11 @@ PackageListView::MessageReceived(BMessage* message) break; } - BAutolock _(fModelLock); + BAutolock _(fModel->Lock()); PackageRow* row = _FindRow(name); if (row != NULL) { if ((changes & PKG_CHANGED_TITLE) != 0) - row->UpdateTitle(); + row->UpdateIconAndTitle(); if ((changes & PKG_CHANGED_SUMMARY) != 0) row->UpdateSummary(); if ((changes & PKG_CHANGED_RATINGS) != 0) @@ -878,7 +875,7 @@ PackageListView::MessageReceived(BMessage* message) if ((changes & PKG_CHANGED_SIZE) != 0) row->UpdateSize(); if ((changes & PKG_CHANGED_ICON) != 0) - row->UpdateTitle(); + row->UpdateIconAndTitle(); if ((changes & PKG_CHANGED_DEPOT) != 0) row->UpdateRepository(); if ((changes & PKG_CHANGED_VERSION) != 0) @@ -927,7 +924,7 @@ PackageListView::AddPackage(const PackageInfoRef& package) if (packageRow != NULL) return; - BAutolock _(fModelLock); + BAutolock _(fModel->Lock()); // create the row for this package packageRow = new PackageRow(package, fPackageListener); diff --git a/src/apps/haikudepot/ui/PackageListView.h b/src/apps/haikudepot/ui/PackageListView.h index 17f4e3f645..866721f9a6 100644 --- a/src/apps/haikudepot/ui/PackageListView.h +++ b/src/apps/haikudepot/ui/PackageListView.h @@ -1,6 +1,7 @@ /* * Copyright 2013, Stephan Aßmus . * Copyright 2013, Rene Gollent . + * Copyright 2020, Andrew Lindesay * All rights reserved. Distributed under the terms of the MIT License. */ #ifndef PACKAGE_LIST_VIEW_H @@ -12,6 +13,7 @@ #include #include +#include "Model.h" #include "PackageInfo.h" @@ -22,7 +24,7 @@ class WorkStatusView; class PackageListView : public BColumnListView { public: - PackageListView(BLocker* modelLock); + PackageListView(Model* model); virtual ~PackageListView(); virtual void AttachedToWindow(); @@ -49,7 +51,7 @@ private: struct RowByNameHashDefinition; typedef BOpenHashTable RowByNameTable; - BLocker* fModelLock; + Model* fModel; ItemCountView* fItemCountView; PackageListener* fPackageListener; RowByNameTable* fRowByNameTable; diff --git a/src/apps/haikudepot/ui/RatePackageWindow.cpp b/src/apps/haikudepot/ui/RatePackageWindow.cpp index 5919c62ca8..885417dfd0 100644 --- a/src/apps/haikudepot/ui/RatePackageWindow.cpp +++ b/src/apps/haikudepot/ui/RatePackageWindow.cpp @@ -154,8 +154,8 @@ protected: virtual const BBitmap* StarBitmap() { if (fRatingDeterminate) - return fStarBlueBitmap->Bitmap(SharedBitmap::SIZE_16); - return fStarGrayBitmap->Bitmap(SharedBitmap::SIZE_16); + return fStarBlueBitmap->Bitmap(BITMAP_SIZE_16); + return fStarGrayBitmap->Bitmap(BITMAP_SIZE_16); } private: diff --git a/src/apps/haikudepot/ui/ScreenshotWindow.cpp b/src/apps/haikudepot/ui/ScreenshotWindow.cpp index 713eae372b..ae5fe67713 100644 --- a/src/apps/haikudepot/ui/ScreenshotWindow.cpp +++ b/src/apps/haikudepot/ui/ScreenshotWindow.cpp @@ -57,10 +57,10 @@ ScreenshotWindow::ScreenshotWindow(BWindow* parent, BRect frame) fToolBar = new BToolBar(); fToolBar->AddAction(MSG_PREVIOUS_SCREENSHOT, this, - sNextButtonIcon->Bitmap(SharedBitmap::SIZE_22), + sNextButtonIcon->Bitmap(BITMAP_SIZE_22), NULL, NULL); fToolBar->AddAction(MSG_NEXT_SCREENSHOT, this, - sPreviousButtonIcon->Bitmap(SharedBitmap::SIZE_22), + sPreviousButtonIcon->Bitmap(BITMAP_SIZE_22), NULL, NULL); fToolBar->AddView(fIndexView); fToolBar->AddGlue(); diff --git a/src/apps/haikudepot/ui_generic/BitmapView.cpp b/src/apps/haikudepot/ui_generic/BitmapView.cpp index ccdeab1087..6b70d0d9ee 100644 --- a/src/apps/haikudepot/ui_generic/BitmapView.cpp +++ b/src/apps/haikudepot/ui_generic/BitmapView.cpp @@ -118,7 +118,7 @@ BitmapView::MaxSize() void -BitmapView::SetBitmap(SharedBitmap* bitmap, SharedBitmap::Size bitmapSize) +BitmapView::SetBitmap(SharedBitmap* bitmap, BitmapSize bitmapSize) { if (bitmap == fReference && bitmapSize == fBitmapSize) return; diff --git a/src/apps/haikudepot/ui_generic/BitmapView.h b/src/apps/haikudepot/ui_generic/BitmapView.h index 117813692f..2e67d6f573 100644 --- a/src/apps/haikudepot/ui_generic/BitmapView.h +++ b/src/apps/haikudepot/ui_generic/BitmapView.h @@ -25,14 +25,13 @@ public: virtual BSize MaxSize(); void SetBitmap(SharedBitmap* bitmap, - SharedBitmap::Size bitmapSize - = SharedBitmap::SIZE_ANY); + BitmapSize bitmapSize = BITMAP_SIZE_ANY); void UnsetBitmap(); void SetScaleBitmap(bool scaleBitmap); private: BitmapRef fReference; - SharedBitmap::Size fBitmapSize; + BitmapSize fBitmapSize; const BBitmap* fBitmap; bool fScaleBitmap; }; diff --git a/src/apps/haikudepot/ui_generic/RatingView.cpp b/src/apps/haikudepot/ui_generic/RatingView.cpp index 74bfae265e..53bb34d4c4 100644 --- a/src/apps/haikudepot/ui_generic/RatingView.cpp +++ b/src/apps/haikudepot/ui_generic/RatingView.cpp @@ -47,8 +47,8 @@ const BBitmap* RatingView::StarBitmap() { if (fRating < RATING_MIN) - return fStarGrayBitmap->Bitmap(SharedBitmap::SIZE_16); - return fStarBlueBitmap->Bitmap(SharedBitmap::SIZE_16); + return fStarGrayBitmap->Bitmap(BITMAP_SIZE_16); + return fStarBlueBitmap->Bitmap(BITMAP_SIZE_16); } diff --git a/src/apps/haikudepot/ui_generic/SharedBitmap.cpp b/src/apps/haikudepot/ui_generic/SharedBitmap.cpp index 21b328d394..a3baa9fbf4 100644 --- a/src/apps/haikudepot/ui_generic/SharedBitmap.cpp +++ b/src/apps/haikudepot/ui_generic/SharedBitmap.cpp @@ -75,41 +75,28 @@ SharedBitmap::SharedBitmap(BPositionIO& data) fSize(0), fMimeType() { - status_t status = data.GetSize(&fSize); - const off_t kMaxSize = 1024 * 1024; - if (status == B_OK && fSize > 0 && fSize <= kMaxSize) { - fBuffer = new(std::nothrow) uint8[fSize]; - if (fBuffer != NULL) { - data.Seek(0, SEEK_SET); - - off_t bytesRead = 0; - size_t chunkSize = std::min((off_t)4096, fSize); - while (bytesRead < fSize) { - ssize_t read = data.Read(fBuffer + bytesRead, chunkSize); - if (read > 0) - bytesRead += read; - else - break; - } - - if (bytesRead != fSize) { - delete[] fBuffer; - fBuffer = NULL; - fSize = 0; - } - } else - fSize = 0; - } else { - HDERROR("SharedBitmap(): Stream too large: %" B_PRIi64 - ", max: %" B_PRIi64, fSize, kMaxSize); + off_t size; + if (data.GetSize(&size) == B_OK) { + data.Seek(0, SEEK_SET); + _InitWithData(data, size); } - fBitmap[0] = NULL; fBitmap[1] = NULL; fBitmap[2] = NULL; fBitmap[3] = NULL; } +SharedBitmap::SharedBitmap(BDataIO& data, size_t size) + : + BReferenceable(), + fResourceID(-1), + fBuffer(NULL), + fSize(0), + fMimeType() +{ + _InitWithData(data, size); +} + SharedBitmap::~SharedBitmap() { @@ -121,8 +108,35 @@ SharedBitmap::~SharedBitmap() } +void +SharedBitmap::_InitWithData(BDataIO& data, size_t size) +{ + fSize = size; + const off_t kMaxSize = 1024 * 1024; + if (size > 0 && size <= kMaxSize) { + fBuffer = new(std::nothrow) uint8[size]; + if (fBuffer != NULL) { + if (data.ReadExactly(fBuffer, size) == B_OK) + fSize = size; + else { + delete[] fBuffer; + fBuffer = NULL; + } + } + } else { + HDERROR("image data too large to deal with; %" B_PRIi64 + ", max: %" B_PRIi64, fSize, kMaxSize); + } + + fBitmap[0] = NULL; + fBitmap[1] = NULL; + fBitmap[2] = NULL; + fBitmap[3] = NULL; +} + + const BBitmap* -SharedBitmap::Bitmap(Size which) +SharedBitmap::Bitmap(BitmapSize which) { if (fResourceID == -1 && fMimeType.Length() == 0 && fBuffer == NULL) return fBitmap[0]; @@ -132,20 +146,20 @@ SharedBitmap::Bitmap(Size which) switch (which) { default: - case SIZE_16: + case BITMAP_SIZE_16: break; - case SIZE_22: + case BITMAP_SIZE_22: index = 1; size = 22; break; - case SIZE_32: + case BITMAP_SIZE_32: index = 2; size = 32; break; - case SIZE_64: + case BITMAP_SIZE_64: index = 3; size = 64; break; diff --git a/src/apps/haikudepot/ui_generic/SharedBitmap.h b/src/apps/haikudepot/ui_generic/SharedBitmap.h index e7e58c0ad0..52f6c2af8b 100644 --- a/src/apps/haikudepot/ui_generic/SharedBitmap.h +++ b/src/apps/haikudepot/ui_generic/SharedBitmap.h @@ -1,5 +1,6 @@ /* * Copyright 2013-2014, Stephan Aßmus . + * Copyright 2020, Andrew Lindesay * All rights reserved. Distributed under the terms of the MIT License. */ #ifndef SHARED_BITMAP_H @@ -9,32 +10,28 @@ #include #include +#include "HaikuDepotConstants.h" #include "List.h" class BBitmap; +class BDataIO; class BPositionIO; class SharedBitmap : public BReferenceable { public: - enum Size { - SIZE_ANY = -1, - SIZE_16 = 0, - SIZE_22 = 1, - SIZE_32 = 2, - SIZE_64 = 3 - }; - SharedBitmap(BBitmap* bitmap); SharedBitmap(int32 resourceID); SharedBitmap(const char* mimeType); SharedBitmap(BPositionIO& data); + SharedBitmap(BDataIO& data, size_t size); ~SharedBitmap(); - const BBitmap* Bitmap(Size which); + const BBitmap* Bitmap(BitmapSize which); private: + void _InitWithData(BDataIO& data, size_t size); BBitmap* _CreateBitmapFromResource(int32 size) const; BBitmap* _CreateBitmapFromBuffer(int32 size) const; BBitmap* _CreateBitmapFromMimeType(int32 size) const; diff --git a/src/apps/haikudepot/util/DataIOUtils.cpp b/src/apps/haikudepot/util/DataIOUtils.cpp index 855ae363fe..06e36d6724 100644 --- a/src/apps/haikudepot/util/DataIOUtils.cpp +++ b/src/apps/haikudepot/util/DataIOUtils.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2018, Andrew Lindesay . + * Copyright 2018-2020, Andrew Lindesay . * All rights reserved. Distributed under the terms of the MIT License. */ #include "DataIOUtils.h" @@ -8,26 +8,66 @@ #define BUFFER_SIZE 1024 -status_t -DataIOUtils::Copy(BDataIO* target, BDataIO* source, size_t size) +/*static*/ status_t +DataIOUtils::CopyAll(BDataIO* target, BDataIO* source) { status_t result = B_OK; uint8 buffer[BUFFER_SIZE]; + size_t sizeRead = 0; - while (size > 0 && result == B_OK) { - size_t sizeToRead = size; - size_t sizeRead = 0; + do { + result = source->ReadExactly(buffer, BUFFER_SIZE, &sizeRead); - if (sizeToRead > BUFFER_SIZE) - sizeToRead = BUFFER_SIZE; - - result = source->ReadExactly(buffer, sizeToRead, &sizeRead); - - if (result == B_OK) - result = target->WriteExactly(buffer, sizeRead); - - size -= sizeRead; - } + switch (result) + { + case B_OK: + case B_PARTIAL_READ: + result = target->WriteExactly(buffer, sizeRead); + break; + } + } while(result == B_OK && sizeRead > 0); return result; -} \ No newline at end of file +} + + +ConstraintedDataIO::ConstraintedDataIO(BDataIO* delegate, size_t limit) + : + fDelegate(delegate), + fLimit(limit) +{ +} + + +ConstraintedDataIO::~ConstraintedDataIO() +{ +} + + +ssize_t +ConstraintedDataIO::Read(void* buffer, size_t size) +{ + if (size > fLimit) + size = fLimit; + + ssize_t actualRead = fDelegate->Read(buffer, size); + + if (actualRead > 0) + fLimit -= actualRead; + + return actualRead; +} + + +ssize_t +ConstraintedDataIO::Write(const void* buffer, size_t size) +{ + return B_NOT_SUPPORTED; +} + + +status_t +ConstraintedDataIO::Flush() +{ + return B_OK; +} diff --git a/src/apps/haikudepot/util/DataIOUtils.h b/src/apps/haikudepot/util/DataIOUtils.h index 268cd28e1a..887ee2b070 100644 --- a/src/apps/haikudepot/util/DataIOUtils.h +++ b/src/apps/haikudepot/util/DataIOUtils.h @@ -1,5 +1,5 @@ /* - * Copyright 2018, Andrew Lindesay . + * Copyright 2018-2020, Andrew Lindesay . * All rights reserved. Distributed under the terms of the MIT License. */ #ifndef DATA_IO_UTILS_H @@ -10,10 +10,33 @@ class DataIOUtils { - public: - static status_t Copy(BDataIO* target, BDataIO* source, size_t size); + static status_t CopyAll(BDataIO* target, BDataIO* source); +}; + +/*! This is a data source (read only) that is restricted to a certain size. An + example of where this is used is with reading out the data from a tar + stream. The tar logic knows how long the data is and so is able to provide + a client this constrained view of the data that is only able to read a + constrained quantity of data from the delegate data source. This prevents + overflow reads from the tar file. +*/ + +class ConstraintedDataIO : public BDataIO { +public: + ConstraintedDataIO(BDataIO* delegate, + size_t limit); + virtual ~ConstraintedDataIO(); + + virtual ssize_t Read(void* buffer, size_t size); + virtual ssize_t Write(const void* buffer, size_t size); + + virtual status_t Flush(); + +private: + BDataIO* fDelegate; + size_t fLimit; }; diff --git a/src/apps/haikudepot/util/RatingUtils.cpp b/src/apps/haikudepot/util/RatingUtils.cpp index 2eef26d831..5f46411c3f 100644 --- a/src/apps/haikudepot/util/RatingUtils.cpp +++ b/src/apps/haikudepot/util/RatingUtils.cpp @@ -22,9 +22,9 @@ RatingUtils::Draw(BView* target, BPoint at, float value) const BBitmap* star; if (value < RATING_MIN) - star = sStarGrayBitmap->Bitmap(SharedBitmap::SIZE_16); + star = sStarGrayBitmap->Bitmap(BITMAP_SIZE_16); else - star = sStarBlueBitmap->Bitmap(SharedBitmap::SIZE_16); + star = sStarBlueBitmap->Bitmap(BITMAP_SIZE_16); if (star == NULL) { debugger("no star icon found in application resources."); diff --git a/src/apps/haikudepot/util/StorageUtils.cpp b/src/apps/haikudepot/util/StorageUtils.cpp index 2a425400f6..4425ba465f 100644 --- a/src/apps/haikudepot/util/StorageUtils.cpp +++ b/src/apps/haikudepot/util/StorageUtils.cpp @@ -278,3 +278,31 @@ StorageUtils::LocalWorkingDirectoryPath(const BString leaf, BPath& path, return result; } + + +/*static*/ status_t +StorageUtils::SwapExtensionOnPath(BPath& path, const char* extension) +{ + BPath parent; + status_t result = path.GetParent(&parent); + if (result == B_OK) { + path.SetTo(parent.Path(), + SwapExtensionOnPathComponent(path.Leaf(), extension).String()); + } + return result; +} + + +/*static*/ BString +StorageUtils::SwapExtensionOnPathComponent(const char* pathComponent, + const char* extension) +{ + BString result(pathComponent); + int32 lastDot = result.FindLast("."); + if (lastDot != B_ERROR) { + result.Truncate(lastDot); + } + result.Append("."); + result.Append(extension); + return result; +} \ No newline at end of file diff --git a/src/apps/haikudepot/util/StorageUtils.h b/src/apps/haikudepot/util/StorageUtils.h index cd39ebd1d5..2860c38464 100644 --- a/src/apps/haikudepot/util/StorageUtils.h +++ b/src/apps/haikudepot/util/StorageUtils.h @@ -28,6 +28,12 @@ public: bool* exists, bool* isDirectory, off_t* size); + + static status_t SwapExtensionOnPath(BPath& path, + const char* extension); + static BString SwapExtensionOnPathComponent( + const char* pathComponent, + const char* extension); }; #endif // PATH_UTILS_H diff --git a/src/tests/apps/haikudepot/HaikuDepotTestAddon.cpp b/src/tests/apps/haikudepot/HaikuDepotTestAddon.cpp index d9b4d41cb2..8ed7271540 100644 --- a/src/tests/apps/haikudepot/HaikuDepotTestAddon.cpp +++ b/src/tests/apps/haikudepot/HaikuDepotTestAddon.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2017-2019, Andrew Lindesay, apl@lindesay.co.nz + * Copyright 2017-2020, Andrew Lindesay, apl@lindesay.co.nz * Distributed under the terms of the MIT License. */ @@ -10,6 +10,8 @@ #include "DumpExportRepositoryJsonListenerTest.h" #include "ValidationFailureTest.h" #include "ValidationUtilsTest.h" +#include "StorageUtilsTest.h" +#include "TarArchiveServiceTest.h" #include "ListTest.h" BTestSuite* @@ -22,6 +24,8 @@ getTestSuite() ValidationFailureTest::AddTests(*suite); ValidationUtilsTest::AddTests(*suite); ListTest::AddTests(*suite); + StorageUtilsTest::AddTests(*suite); + TarArchiveServiceTest::AddTests(*suite); return suite; } diff --git a/src/tests/apps/haikudepot/Jamfile b/src/tests/apps/haikudepot/Jamfile index a79ab2459e..4f695f14eb 100644 --- a/src/tests/apps/haikudepot/Jamfile +++ b/src/tests/apps/haikudepot/Jamfile @@ -15,6 +15,7 @@ local sourceDirs = server server/dumpexportrepository util + tar textview edits_generic ; @@ -31,6 +32,8 @@ SEARCH_SOURCE += [ FDirName $(HAIKUDEPOT_GENERATED_SOURCES_DIRECTORY) UnitTestLib haikudepottest.so : HaikuDepotTestAddon.cpp + DataIOUtils.cpp + DumpExportRepositorySource.cpp DumpExportRepositorySourceMirror.cpp DumpExportRepository.cpp @@ -45,6 +48,13 @@ UnitTestLib haikudepottest.so : StandardMetaDataJsonEventListener.cpp StandardMetaDataJsonEventListenerTest.cpp + StorageUtils.cpp + StorageUtilsTest.cpp + + TarArchiveHeader.cpp + TarArchiveService.cpp + TarArchiveServiceTest.cpp + ValidationFailure.cpp ValidationFailureTest.cpp diff --git a/src/tests/apps/haikudepot/StorageUtilsTest.cpp b/src/tests/apps/haikudepot/StorageUtilsTest.cpp new file mode 100644 index 0000000000..f9d70ea5e4 --- /dev/null +++ b/src/tests/apps/haikudepot/StorageUtilsTest.cpp @@ -0,0 +1,50 @@ +/* + * Copyright 2020, Andrew Lindesay . + * All rights reserved. Distributed under the terms of the MIT License. + */ +#include "StorageUtilsTest.h" + +#include + +#include +#include + +#include "StorageUtils.h" + + +StorageUtilsTest::StorageUtilsTest() +{ +} + + +StorageUtilsTest::~StorageUtilsTest() +{ +} + + +void +StorageUtilsTest::TestSwapExtensionOnPathComponent() +{ + const BString input = "/paved/path.tea"; + +// ---------------------- + BString output = StorageUtils::SwapExtensionOnPathComponent(input, "chai"); +// ---------------------- + + const BString expected = "/paved/path.chai"; + CPPUNIT_ASSERT_EQUAL(expected, output); +} + + +/*static*/ void +StorageUtilsTest::AddTests(BTestSuite& parent) +{ + CppUnit::TestSuite& suite = *new CppUnit::TestSuite("StorageUtilsTest"); + + suite.addTest( + new CppUnit::TestCaller( + "StorageUtilsTest::TestSwapExtensionOnPathComponent", + &StorageUtilsTest::TestSwapExtensionOnPathComponent)); + + parent.addTest("StorageUtilsTest", &suite); +} \ No newline at end of file diff --git a/src/tests/apps/haikudepot/StorageUtilsTest.h b/src/tests/apps/haikudepot/StorageUtilsTest.h new file mode 100644 index 0000000000..ffae660373 --- /dev/null +++ b/src/tests/apps/haikudepot/StorageUtilsTest.h @@ -0,0 +1,23 @@ +/* + * Copyright 2020, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ +#ifndef STORAGE_UTILS_TEST_H +#define STORAGE_UTILS_TEST_H + +#include +#include + + +class StorageUtilsTest : public CppUnit::TestCase { +public: + StorageUtilsTest(); + virtual ~StorageUtilsTest(); + + void TestSwapExtensionOnPathComponent(); + + static void AddTests(BTestSuite& suite); +}; + + +#endif // STORAGE_UTILS_TEST_H diff --git a/src/tests/apps/haikudepot/TarArchiveServiceTest.cpp b/src/tests/apps/haikudepot/TarArchiveServiceTest.cpp new file mode 100644 index 0000000000..6ec5e7ac08 --- /dev/null +++ b/src/tests/apps/haikudepot/TarArchiveServiceTest.cpp @@ -0,0 +1,102 @@ +/* + * Copyright 2020, Andrew Lindesay . + * All rights reserved. Distributed under the terms of the MIT License. + */ +#include "TarArchiveServiceTest.h" + +#include + +#include +#include +#include + +#include +#include + +#include "TarArchiveHeader.h" +#include "TarArchiveService.h" + +#include "TarArchiveServiceTestSample.h" + + +TarArchiveServiceTest::TarArchiveServiceTest() +{ +} + + +TarArchiveServiceTest::~TarArchiveServiceTest() +{ +} + + +void +TarArchiveServiceTest::TestReadHeader() +{ + BString tarPath; + if (CreateSampleFile(tarPath) != B_OK) { + printf("! unable to setup the sample tar --> exit"); + exit(1); + } + + BFile *tarFile = new BFile(tarPath, O_RDONLY); + tarFile->Seek(2048, SEEK_SET); + // known offset in the same data + + TarArchiveHeader header; + +// ---------------------- + status_t result = TarArchiveService::GetEntry(*tarFile, header); +// ---------------------- + + CPPUNIT_ASSERT_EQUAL(B_OK, result); + CPPUNIT_ASSERT_EQUAL(BString("hicn/somepkg/16.png"), header.FileName()); + CPPUNIT_ASSERT_EQUAL(657, header.Length()); + CPPUNIT_ASSERT_EQUAL(TAR_FILE_TYPE_NORMAL, header.FileType()); + + delete tarFile; + + DeleteSampleFile(tarPath); +} + + +/*! This method will store the tar file sample into a temporary file and will + return the pathname to that file. +*/ + +status_t +TarArchiveServiceTest::CreateSampleFile(BString& path) +{ + path.SetTo(tmpnam(NULL)); + BFile* file = new BFile(path, O_WRONLY | O_CREAT); + ObjectDeleter tarIoDeleter(file); + status_t result = file->WriteExactly(kSampleTarPayload, kSampleTarLength); + + if (result != B_OK) { + printf("! unable to write the sample data to [%s]\n", path.String()); + } + + return result; +} + + +status_t +TarArchiveServiceTest::DeleteSampleFile(const BString& path) +{ + if (remove(path.String()) != 0) + return B_ERROR; + return B_OK; +} + + +/*static*/ void +TarArchiveServiceTest::AddTests(BTestSuite& parent) +{ + CppUnit::TestSuite& suite = *new CppUnit::TestSuite("TarArchiveServiceTest"); + + suite.addTest( + new CppUnit::TestCaller( + "TarArchiveServiceTest::TestReadHeader", + &TarArchiveServiceTest::TestReadHeader)); + + parent.addTest("TarArchiveServiceTest", &suite); +} \ No newline at end of file diff --git a/src/tests/apps/haikudepot/TarArchiveServiceTest.h b/src/tests/apps/haikudepot/TarArchiveServiceTest.h new file mode 100644 index 0000000000..b57f760faa --- /dev/null +++ b/src/tests/apps/haikudepot/TarArchiveServiceTest.h @@ -0,0 +1,28 @@ +/* + * Copyright 2020, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ +#ifndef TAR_ARCHIVE_SERVICE_TEST_H +#define TAR_ARCHIVE_SERVICE_TEST_H + +#include "String.h" + +#include +#include + + +class TarArchiveServiceTest : public CppUnit::TestCase { +public: + TarArchiveServiceTest(); + virtual ~TarArchiveServiceTest(); + + void TestReadHeader(); + + static void AddTests(BTestSuite& suite); +private: + status_t CreateSampleFile(BString& path); + status_t DeleteSampleFile(const BString& path); +}; + + +#endif // TAR_ARCHIVE_SERVICE_TEST_H diff --git a/src/tests/apps/haikudepot/TarArchiveServiceTestSample.h b/src/tests/apps/haikudepot/TarArchiveServiceTestSample.h new file mode 100644 index 0000000000..47f9fbbd3a --- /dev/null +++ b/src/tests/apps/haikudepot/TarArchiveServiceTestSample.h @@ -0,0 +1,876 @@ +/* + * Copyright 2020, Andrew Lindesay + * Distributed under the terms of the MIT License. + */ + +// This file contains sample data for the testing of the TarArchiveService. +// ----- + +// The following tar data contains a structure similar to that which would be +// returned from HDS when it provides a dump of all of the icons in the system. +// The tar contains the following files and (file-lengths) and (offsets); +// +// hicn/ (0) (0) +// hicn/info.json (212) (512) +// hicn/somepkg/ (0) (1536) +// hicn/somepkg/16.png (657) (2048) +// hicn/somepkg/icon.hvif (64) (3584) +// hicn/somepkg/32.png (1160) (4608) + +unsigned char kSampleTarPayload[] = { + 0x68, 0x69, 0x63, 0x6e, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x37, 0x37, 0x35, 0x00, + 0x30, 0x30, 0x30, 0x31, 0x37, 0x35, 0x30, 0x00, 0x30, 0x30, 0x30, 0x31, + 0x37, 0x35, 0x30, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x00, 0x31, 0x33, 0x37, 0x32, 0x36, 0x31, 0x33, 0x32, + 0x36, 0x36, 0x33, 0x00, 0x30, 0x31, 0x32, 0x35, 0x34, 0x30, 0x00, 0x20, + 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x73, 0x74, 0x61, 0x72, 0x20, 0x20, + 0x00, 0x61, 0x6e, 0x64, 0x70, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x6e, 0x64, + 0x70, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x69, 0x63, 0x6e, + 0x2f, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x6a, 0x73, 0x6f, 0x6e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x30, 0x30, 0x30, 0x36, 0x36, 0x34, 0x00, 0x30, 0x30, 0x30, 0x31, + 0x37, 0x35, 0x30, 0x00, 0x30, 0x30, 0x30, 0x31, 0x37, 0x35, 0x30, 0x00, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x33, 0x32, 0x34, 0x00, + 0x31, 0x33, 0x37, 0x32, 0x36, 0x31, 0x33, 0x32, 0x36, 0x36, 0x33, 0x00, + 0x30, 0x31, 0x34, 0x33, 0x36, 0x35, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x75, 0x73, 0x74, 0x61, 0x72, 0x20, 0x20, 0x00, 0x61, 0x6e, 0x64, + 0x70, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x6e, 0x64, 0x70, 0x6f, 0x6e, 0x6c, + 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7b, 0x22, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x3a, 0x31, + 0x35, 0x39, 0x39, 0x31, 0x32, 0x37, 0x37, 0x39, 0x30, 0x39, 0x32, 0x34, + 0x2c, 0x22, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x49, 0x73, 0x6f, 0x22, 0x3a, 0x22, 0x32, + 0x30, 0x32, 0x30, 0x2d, 0x30, 0x39, 0x2d, 0x30, 0x33, 0x20, 0x31, 0x30, + 0x3a, 0x30, 0x39, 0x3a, 0x35, 0x30, 0x22, 0x2c, 0x22, 0x64, 0x61, 0x74, + 0x61, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x3a, 0x31, 0x35, 0x39, 0x36, + 0x39, 0x35, 0x36, 0x30, 0x35, 0x31, 0x30, 0x30, 0x30, 0x2c, 0x22, 0x64, + 0x61, 0x74, 0x61, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x49, 0x73, 0x6f, 0x22, + 0x3a, 0x22, 0x32, 0x30, 0x32, 0x30, 0x2d, 0x30, 0x38, 0x2d, 0x30, 0x39, + 0x20, 0x30, 0x36, 0x3a, 0x35, 0x34, 0x3a, 0x31, 0x31, 0x22, 0x2c, 0x22, + 0x61, 0x67, 0x65, 0x6e, 0x74, 0x22, 0x3a, 0x22, 0x68, 0x64, 0x73, 0x22, + 0x2c, 0x22, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x22, 0x3a, 0x22, 0x31, 0x2e, 0x30, 0x2e, 0x31, 0x32, 0x32, + 0x2d, 0x53, 0x4e, 0x41, 0x50, 0x53, 0x48, 0x4f, 0x54, 0x22, 0x7d, 0x0a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x68, 0x69, 0x63, 0x6e, 0x2f, 0x73, 0x6f, 0x6d, 0x65, 0x70, 0x6b, 0x67, + 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x37, 0x37, 0x35, 0x00, + 0x30, 0x30, 0x30, 0x31, 0x37, 0x35, 0x30, 0x00, 0x30, 0x30, 0x30, 0x31, + 0x37, 0x35, 0x30, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x00, 0x31, 0x33, 0x37, 0x32, 0x36, 0x31, 0x33, 0x32, + 0x33, 0x36, 0x32, 0x00, 0x30, 0x31, 0x34, 0x32, 0x30, 0x31, 0x00, 0x20, + 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x73, 0x74, 0x61, 0x72, 0x20, 0x20, + 0x00, 0x61, 0x6e, 0x64, 0x70, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x6e, 0x64, + 0x70, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x69, 0x63, 0x6e, + 0x2f, 0x73, 0x6f, 0x6d, 0x65, 0x70, 0x6b, 0x67, 0x2f, 0x31, 0x36, 0x2e, + 0x70, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x30, 0x30, 0x30, 0x36, 0x34, 0x34, 0x00, 0x30, 0x30, 0x30, 0x31, + 0x37, 0x35, 0x30, 0x00, 0x30, 0x30, 0x30, 0x31, 0x37, 0x35, 0x30, 0x00, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x32, 0x32, 0x31, 0x00, + 0x31, 0x33, 0x37, 0x32, 0x36, 0x31, 0x33, 0x32, 0x33, 0x35, 0x35, 0x00, + 0x30, 0x31, 0x35, 0x31, 0x33, 0x31, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x75, 0x73, 0x74, 0x61, 0x72, 0x20, 0x20, 0x00, 0x61, 0x6e, 0x64, + 0x70, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x6e, 0x64, 0x70, 0x6f, 0x6e, 0x6c, + 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, + 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x10, 0x08, 0x03, 0x00, 0x00, 0x00, 0x28, 0x2d, 0x0f, + 0x53, 0x00, 0x00, 0x00, 0x03, 0x73, 0x42, 0x49, 0x54, 0x08, 0x08, 0x08, + 0xdb, 0xe1, 0x4f, 0xe0, 0x00, 0x00, 0x01, 0x32, 0x50, 0x4c, 0x54, 0x45, + 0x30, 0x0b, 0x3e, 0xe1, 0xb5, 0xf1, 0xb2, 0x42, 0xdb, 0x8e, 0x22, 0xb6, + 0x7f, 0x7f, 0x7f, 0xc9, 0x7b, 0xe6, 0x92, 0x92, 0x92, 0x66, 0x66, 0x66, + 0xff, 0xff, 0xff, 0x7f, 0x1e, 0xa2, 0x6d, 0x1a, 0x8b, 0xd9, 0xd9, 0xd9, + 0xe5, 0xe5, 0xe5, 0xc5, 0xc5, 0xc5, 0x65, 0x18, 0x81, 0xbe, 0x60, 0xe0, + 0x91, 0x68, 0xa0, 0xac, 0x33, 0xd8, 0xd9, 0xa1, 0xed, 0x45, 0x45, 0x45, + 0xa0, 0x26, 0xcc, 0xf9, 0xf9, 0xf9, 0x4d, 0x12, 0x63, 0x8a, 0x6a, 0x96, + 0xe8, 0xc6, 0xf4, 0x6d, 0x4a, 0x7a, 0xb1, 0xb1, 0xb1, 0x4c, 0x12, 0x62, + 0xf4, 0xf4, 0xf4, 0xb8, 0x51, 0xdd, 0xab, 0xab, 0xab, 0xd4, 0xd4, 0xd4, + 0xc2, 0x69, 0xe2, 0x8e, 0x2c, 0xb1, 0x97, 0x24, 0xc1, 0xed, 0xd4, 0xf6, + 0xa8, 0x29, 0xd6, 0xcf, 0x8b, 0xe8, 0xdd, 0xac, 0xef, 0xe4, 0xbd, 0xf2, + 0xae, 0x38, 0xd9, 0x87, 0x20, 0xad, 0x73, 0x1c, 0x93, 0xb5, 0x49, 0xdc, + 0xba, 0x55, 0xde, 0x84, 0x2a, 0xa5, 0x93, 0x72, 0xa0, 0xc4, 0x6e, 0xe3, + 0x75, 0x55, 0x81, 0xc0, 0x66, 0xe1, 0x6b, 0x1a, 0x88, 0x54, 0x14, 0x6b, + 0xb0, 0x3d, 0xda, 0xaa, 0x2e, 0xd7, 0xcc, 0x81, 0xe7, 0x9a, 0x25, 0xc5, + 0x6a, 0x6a, 0x6a, 0xe7, 0xc4, 0xf3, 0x90, 0x6b, 0x9e, 0x77, 0x1d, 0x98, + 0xc7, 0x75, 0xe4, 0x96, 0x69, 0xa6, 0xdf, 0xb0, 0xf0, 0xf0, 0xda, 0xf8, + 0xd2, 0x91, 0xea, 0x71, 0x1b, 0x91, 0xb6, 0x4c, 0xdc, 0x92, 0x23, 0xbb, + 0xb9, 0x54, 0xde, 0x9d, 0x26, 0xc8, 0xfb, 0xfb, 0xfb, 0x83, 0x1f, 0xa7, + 0xb3, 0x46, 0xdb, 0xbb, 0x58, 0xdf, 0xda, 0xa5, 0xed, 0x6f, 0x1b, 0x8e, + 0xad, 0x36, 0xd8, 0xe9, 0xc9, 0xf4, 0xab, 0x32, 0xd7, 0xf7, 0xf7, 0xf7, + 0xc3, 0x6d, 0xe3, 0xaf, 0x3c, 0xd9, 0xb1, 0x41, 0xda, 0xcd, 0x84, 0xe7, + 0xa2, 0x27, 0xcf, 0xb9, 0x50, 0xdf, 0x99, 0x24, 0xc3, 0xc1, 0x66, 0xe3, + 0xaf, 0x3a, 0xd9, 0xb5, 0x4a, 0xdc, 0xac, 0x34, 0xd8, 0xb3, 0x44, 0xdb, + 0xba, 0x56, 0xdf, 0xe1, 0xb7, 0xf1, 0xc3, 0x6a, 0xe3, 0xa8, 0x2a, 0xd6, + 0xb0, 0x3e, 0xda, 0xe7, 0xc5, 0xf3, 0xdf, 0xb3, 0xf1, 0xb7, 0x4e, 0xdd, + 0xb5, 0x46, 0xdb, 0xe9, 0xcb, 0xf5, 0xf3, 0x42, 0xfd, 0x0e, 0x00, 0x00, + 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x12, 0x00, 0x00, + 0x0b, 0x12, 0x01, 0xd2, 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x00, 0x1c, 0x74, + 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x00, + 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x69, 0x72, 0x65, 0x77, 0x6f, + 0x72, 0x6b, 0x73, 0x20, 0x43, 0x53, 0x35, 0x71, 0xb5, 0xe3, 0x36, 0x00, + 0x00, 0x00, 0xce, 0x49, 0x44, 0x41, 0x54, 0x18, 0x95, 0x63, 0xe0, 0x40, + 0x03, 0x0c, 0x60, 0xd2, 0xdf, 0xd6, 0x43, 0x8f, 0x1b, 0x49, 0x40, 0x3e, + 0xde, 0xd7, 0x57, 0x8d, 0x5f, 0x97, 0x17, 0x2e, 0xa0, 0x62, 0xaf, 0xac, + 0xae, 0x26, 0x64, 0xc3, 0x02, 0x17, 0x70, 0x8e, 0x51, 0xf5, 0xf2, 0xd2, + 0xf7, 0x81, 0x0b, 0xb8, 0x29, 0xb9, 0x44, 0x9a, 0x24, 0xfb, 0x04, 0x41, + 0x05, 0xfc, 0x05, 0x4c, 0xcd, 0x3d, 0x65, 0x23, 0xa3, 0x02, 0xb5, 0x19, + 0x40, 0xe6, 0x32, 0xc8, 0x2b, 0x25, 0x59, 0x72, 0x7a, 0x7a, 0xca, 0x46, + 0x7b, 0x38, 0x0b, 0x32, 0x29, 0xf2, 0x72, 0x30, 0x98, 0xab, 0x4b, 0x48, + 0xd8, 0x25, 0x98, 0xcb, 0x46, 0x32, 0x6b, 0x98, 0x31, 0x86, 0xb3, 0x70, + 0x30, 0xb8, 0x2a, 0x38, 0x30, 0x5a, 0xf2, 0x8b, 0xb9, 0x5b, 0xfb, 0x05, + 0x33, 0xea, 0x18, 0x01, 0x05, 0x42, 0x0c, 0x0d, 0x0d, 0x65, 0xb5, 0xf8, + 0x34, 0x3d, 0x0c, 0xf5, 0x9d, 0x8c, 0x40, 0x02, 0x51, 0x01, 0x86, 0x86, + 0x81, 0x5a, 0x8e, 0x9a, 0x9e, 0xfa, 0xfa, 0x81, 0x46, 0x46, 0xc2, 0x32, + 0x0c, 0x22, 0xac, 0x71, 0x40, 0x01, 0x63, 0x2e, 0x59, 0x90, 0x80, 0x98, + 0x85, 0x14, 0x03, 0x8f, 0x95, 0x29, 0x7f, 0xa0, 0xb1, 0x05, 0x9b, 0x41, + 0x88, 0x61, 0xa0, 0x18, 0xbb, 0x9c, 0x28, 0xd0, 0x61, 0x3c, 0xe2, 0xde, + 0x16, 0x72, 0xa2, 0x1c, 0x3c, 0x92, 0xde, 0x40, 0x3e, 0xc4, 0xe9, 0xa2, + 0x10, 0x8f, 0x82, 0x29, 0x00, 0xe6, 0xfb, 0x21, 0xfd, 0x7c, 0x2e, 0x82, + 0xe5, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, + 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x69, 0x63, 0x6e, + 0x2f, 0x73, 0x6f, 0x6d, 0x65, 0x70, 0x6b, 0x67, 0x2f, 0x69, 0x63, 0x6f, + 0x6e, 0x2e, 0x68, 0x76, 0x69, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x30, 0x30, 0x30, 0x36, 0x34, 0x34, 0x00, 0x30, 0x30, 0x30, 0x31, + 0x37, 0x35, 0x30, 0x00, 0x30, 0x30, 0x30, 0x31, 0x37, 0x35, 0x30, 0x00, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x30, 0x00, + 0x31, 0x33, 0x37, 0x32, 0x36, 0x31, 0x33, 0x32, 0x33, 0x32, 0x31, 0x00, + 0x30, 0x31, 0x35, 0x37, 0x36, 0x37, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x75, 0x73, 0x74, 0x61, 0x72, 0x20, 0x20, 0x00, 0x61, 0x6e, 0x64, + 0x70, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x6e, 0x64, 0x70, 0x6f, 0x6e, 0x6c, + 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6e, 0x63, 0x69, 0x66, 0x01, 0x03, 0xff, 0xaa, + 0x00, 0x01, 0x02, 0x07, 0x38, 0x2d, 0x42, 0x25, 0x2e, 0x35, 0x2c, 0x40, + 0x29, 0x3d, 0x2f, 0x43, 0x38, 0x44, 0x36, 0x3f, 0x3a, 0x49, 0x33, 0x50, + 0x2d, 0x4b, 0x39, 0x55, 0x57, 0x4a, 0x54, 0x53, 0x5a, 0x41, 0x50, 0x3e, + 0x51, 0x42, 0x4f, 0x3a, 0xc5, 0xfd, 0xb9, 0x33, 0xc7, 0x73, 0xbc, 0x37, + 0x4c, 0x28, 0x01, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x68, 0x69, 0x63, 0x6e, 0x2f, 0x73, 0x6f, 0x6d, 0x65, 0x70, 0x6b, 0x67, + 0x2f, 0x33, 0x32, 0x2e, 0x70, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x36, 0x34, 0x34, 0x00, + 0x30, 0x30, 0x30, 0x31, 0x37, 0x35, 0x30, 0x00, 0x30, 0x30, 0x30, 0x31, + 0x37, 0x35, 0x30, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x32, + 0x32, 0x31, 0x30, 0x00, 0x31, 0x33, 0x37, 0x32, 0x36, 0x31, 0x33, 0x32, + 0x33, 0x36, 0x32, 0x00, 0x30, 0x31, 0x35, 0x31, 0x32, 0x34, 0x00, 0x20, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x73, 0x74, 0x61, 0x72, 0x20, 0x20, + 0x00, 0x61, 0x6e, 0x64, 0x70, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x6e, 0x64, + 0x70, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x50, 0x4e, 0x47, + 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x08, 0x03, 0x00, 0x00, + 0x00, 0x44, 0xa4, 0x8a, 0xc6, 0x00, 0x00, 0x00, 0x03, 0x73, 0x42, 0x49, + 0x54, 0x08, 0x08, 0x08, 0xdb, 0xe1, 0x4f, 0xe0, 0x00, 0x00, 0x01, 0xec, + 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0x00, 0xb6, 0x90, 0xea, 0x6d, 0x27, + 0xcf, 0x66, 0x66, 0x66, 0x48, 0x1a, 0x88, 0xfa, 0xfa, 0xfa, 0x99, 0x65, + 0xe1, 0x2c, 0x10, 0x54, 0xd7, 0xc3, 0xf3, 0xd4, 0xd4, 0xd4, 0x51, 0x51, + 0x51, 0x99, 0x99, 0x99, 0x86, 0x49, 0xdc, 0x17, 0x17, 0x17, 0xe4, 0xd7, + 0xf7, 0x4e, 0x1c, 0x93, 0x29, 0x29, 0x29, 0xac, 0x81, 0xe7, 0xcb, 0xb1, + 0xf0, 0x53, 0x1e, 0x9e, 0xa0, 0xa0, 0xa0, 0x3a, 0x14, 0x6e, 0x84, 0x84, + 0x84, 0x7e, 0x3c, 0xda, 0x8d, 0x54, 0xde, 0x9e, 0x6c, 0xe3, 0x20, 0x0b, + 0x3e, 0x14, 0x14, 0x14, 0xf0, 0xf0, 0xf0, 0x83, 0x83, 0x83, 0xa9, 0xa9, + 0xa9, 0x7e, 0x7e, 0x7e, 0x77, 0x32, 0xd8, 0xc2, 0xa3, 0xed, 0x36, 0x13, + 0x68, 0x24, 0x24, 0x24, 0x60, 0x22, 0xb6, 0x7b, 0x7b, 0x7b, 0x2b, 0x16, + 0x47, 0xa2, 0x72, 0xe4, 0x93, 0x5c, 0xe0, 0xef, 0xef, 0xef, 0x3a, 0x3a, + 0x3a, 0xd1, 0xba, 0xf1, 0x1c, 0x1c, 0x1c, 0x3e, 0x16, 0x76, 0xb7, 0xb7, + 0xb7, 0x81, 0x42, 0xda, 0xff, 0xff, 0xff, 0xe5, 0xe5, 0xe5, 0xbd, 0x9b, + 0xec, 0x8a, 0x4e, 0xdd, 0xdd, 0xcb, 0xf5, 0x4a, 0x1a, 0x8d, 0x59, 0x20, + 0xaa, 0x05, 0x05, 0x05, 0x7b, 0x38, 0xd9, 0x34, 0x12, 0x63, 0x45, 0x18, + 0x83, 0xc7, 0xab, 0xee, 0xe9, 0xdf, 0xf8, 0x9b, 0x68, 0xe2, 0xf5, 0xf5, + 0xf5, 0x77, 0x32, 0xd7, 0xb2, 0xb2, 0xb2, 0xdf, 0xdf, 0xdf, 0xd0, 0xb9, + 0xf1, 0x91, 0x59, 0xdf, 0xa9, 0x7d, 0xe6, 0xaf, 0x87, 0xe8, 0x8a, 0x8a, + 0x8a, 0xd5, 0xc0, 0xf3, 0xbc, 0x9b, 0xeb, 0x20, 0x20, 0x20, 0x84, 0x46, + 0xdb, 0x9e, 0x9e, 0x9e, 0xc1, 0xa1, 0xed, 0x4f, 0x1c, 0x97, 0xcf, 0xb6, + 0xf1, 0x62, 0x23, 0xbb, 0x4c, 0x1a, 0x93, 0x88, 0x4c, 0xdd, 0xd9, 0xc5, + 0xf4, 0x57, 0x57, 0x57, 0xe0, 0xd1, 0xf6, 0x8c, 0x51, 0xde, 0x79, 0x36, + 0xd8, 0xae, 0x86, 0xe7, 0x9a, 0x66, 0xe2, 0xec, 0xe3, 0xf9, 0xa0, 0x71, + 0xe3, 0xe7, 0xdb, 0xf8, 0x31, 0x11, 0x5d, 0xc5, 0xa7, 0xee, 0xa4, 0x76, + 0xe5, 0xf9, 0xf9, 0xf9, 0x95, 0x60, 0xe0, 0xb1, 0x89, 0xe8, 0xff, 0xff, + 0xff, 0x7f, 0x3e, 0xda, 0x88, 0x88, 0x88, 0x57, 0x1f, 0xa5, 0x41, 0x17, + 0x7c, 0x24, 0x0d, 0x44, 0x5d, 0x21, 0xb0, 0x8f, 0x56, 0xdf, 0xc9, 0xad, + 0xef, 0x91, 0x5a, 0xdf, 0x08, 0x08, 0x08, 0x9c, 0x6a, 0xe2, 0x3c, 0x15, + 0x72, 0x9f, 0x6e, 0xe3, 0x7d, 0x3c, 0xd9, 0x64, 0x24, 0xbe, 0xcd, 0xb4, + 0xf0, 0x83, 0x42, 0xdb, 0x81, 0x40, 0xdb, 0xb3, 0x8d, 0xe9, 0x7a, 0x36, + 0xd9, 0x99, 0x66, 0xe1, 0x87, 0x4a, 0xdc, 0x38, 0x14, 0x6b, 0x2e, 0x10, + 0x58, 0x2d, 0x16, 0x4b, 0x46, 0x19, 0x86, 0xd8, 0xc5, 0xf3, 0x6e, 0x27, + 0xd1, 0xd3, 0xbb, 0xf3, 0x52, 0x52, 0x52, 0x78, 0x34, 0xd8, 0xd5, 0xc1, + 0xf3, 0x97, 0x60, 0xe1, 0x94, 0x5e, 0xe0, 0x8f, 0x54, 0xdf, 0x51, 0x1d, + 0x99, 0x7c, 0x7c, 0x7c, 0x06, 0x06, 0x06, 0xee, 0xe5, 0xfa, 0xa6, 0x79, + 0xe5, 0x93, 0x5a, 0xdf, 0x5b, 0x20, 0xad, 0xad, 0x81, 0xe7, 0xeb, 0xe1, + 0xf9, 0xe9, 0xdd, 0xf9, 0xc9, 0xaf, 0xef, 0x48, 0x1a, 0x8b, 0xd9, 0xc7, + 0xf5, 0x8b, 0x50, 0xdd, 0x83, 0x44, 0xdb, 0x8d, 0x52, 0xdf, 0xc1, 0xa3, + 0xed, 0xcf, 0xb7, 0xf1, 0xc3, 0xa5, 0xed, 0xa3, 0x74, 0xe4, 0xbd, 0x9d, + 0xed, 0xc5, 0xa9, 0xef, 0xd1, 0xbb, 0xf3, 0xcd, 0xb5, 0xf1, 0xb7, 0x93, + 0xea, 0x54, 0x1e, 0x9f, 0xa9, 0x7e, 0xe6, 0x97, 0x62, 0xe1, 0x93, 0x5c, + 0xdf, 0xad, 0x83, 0xe7, 0xda, 0xf7, 0xd7, 0x2d, 0x00, 0x00, 0x00, 0x09, + 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x12, 0x00, 0x00, 0x0b, 0x12, + 0x01, 0xd2, 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x00, 0x1c, 0x74, 0x45, 0x58, + 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x00, 0x41, 0x64, + 0x6f, 0x62, 0x65, 0x20, 0x46, 0x69, 0x72, 0x65, 0x77, 0x6f, 0x72, 0x6b, + 0x73, 0x20, 0x43, 0x53, 0x35, 0x71, 0xb5, 0xe3, 0x36, 0x00, 0x00, 0x02, + 0x0b, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0x85, 0xd3, 0xfb, 0x4f, 0xd3, + 0x50, 0x14, 0x07, 0xf0, 0x23, 0xb4, 0x13, 0x65, 0x58, 0xc0, 0xe1, 0x3a, + 0x51, 0x44, 0x37, 0x5f, 0xc0, 0xec, 0x5c, 0x9d, 0x6e, 0xe2, 0xa6, 0x74, + 0x6e, 0x85, 0x5a, 0x20, 0xad, 0x52, 0xe6, 0x74, 0x53, 0x53, 0x5f, 0x0c, + 0x4b, 0x11, 0x63, 0xba, 0xa9, 0xdb, 0x30, 0x21, 0xbe, 0x42, 0x4c, 0x8c, + 0x26, 0xb2, 0x1f, 0x7c, 0xff, 0xa3, 0xde, 0xdb, 0xae, 0xdd, 0xd8, 0x86, + 0x9e, 0xdf, 0x4e, 0xbf, 0x9f, 0x9c, 0x9b, 0x9c, 0x7b, 0x0b, 0xa1, 0xff, + 0x14, 0x6c, 0x6f, 0x13, 0xff, 0x06, 0xe4, 0xf1, 0x0b, 0x7b, 0xcf, 0x90, + 0x3b, 0x83, 0xa9, 0xb1, 0xcb, 0xfc, 0xcf, 0xc7, 0x33, 0x07, 0xce, 0xee, + 0x00, 0x46, 0x5e, 0x2e, 0xde, 0xba, 0x21, 0x57, 0x77, 0x29, 0x2f, 0xd6, + 0x9b, 0xc6, 0x38, 0x20, 0xd1, 0x7d, 0x9e, 0xff, 0xb8, 0xef, 0x1c, 0x3f, + 0x57, 0xc8, 0x4d, 0x2f, 0xef, 0x9f, 0x69, 0x07, 0xfd, 0xc9, 0x9b, 0xef, + 0xd1, 0x80, 0xf1, 0xc1, 0xc9, 0x2d, 0x31, 0x5c, 0x83, 0x76, 0x00, 0xcf, + 0xe5, 0x4b, 0xd6, 0x00, 0x31, 0xec, 0x53, 0x3a, 0x01, 0xb6, 0xa8, 0x1d, + 0xe5, 0xf1, 0x80, 0xb0, 0x4b, 0xe9, 0x08, 0x68, 0xb6, 0xf8, 0x8c, 0x1d, + 0x10, 0x73, 0xe1, 0xdf, 0x8a, 0xb2, 0xda, 0x09, 0x2c, 0x61, 0xe1, 0x8d, + 0xc6, 0xe9, 0x8a, 0xb2, 0xca, 0xb4, 0x81, 0x44, 0x08, 0x92, 0x75, 0x51, + 0x41, 0x03, 0x84, 0x16, 0x40, 0xbe, 0x7d, 0xb7, 0x0e, 0x1b, 0x75, 0x81, + 0x73, 0x09, 0xc8, 0x66, 0x30, 0x35, 0xd6, 0xb5, 0x15, 0x87, 0xbb, 0x48, + 0x2c, 0x20, 0x81, 0x0e, 0x70, 0xb3, 0xe0, 0xec, 0x13, 0x81, 0x91, 0x13, + 0xbe, 0x72, 0xb9, 0x0a, 0xbf, 0x2c, 0xe1, 0x46, 0xb9, 0x34, 0x3f, 0xa4, + 0x3c, 0x38, 0x68, 0xed, 0x13, 0x12, 0xdd, 0x6f, 0x44, 0x59, 0x96, 0x7b, + 0x28, 0x8a, 0xb1, 0x04, 0x23, 0x48, 0xf3, 0x2b, 0xc2, 0x67, 0x57, 0x4e, + 0xdf, 0xc4, 0xfb, 0x84, 0x7e, 0x2f, 0x2f, 0xe3, 0xe2, 0xf3, 0x11, 0x4a, + 0xc5, 0x42, 0x90, 0x02, 0x2b, 0x12, 0x9d, 0x5d, 0xe3, 0xbe, 0x66, 0xf0, + 0xf9, 0x00, 0x03, 0x93, 0x65, 0x53, 0xf4, 0xac, 0xc5, 0x2f, 0x46, 0xd4, + 0xe4, 0x92, 0x3b, 0x10, 0x64, 0x97, 0xaf, 0xce, 0x71, 0xdf, 0x68, 0xc2, + 0x02, 0x86, 0x51, 0x13, 0xcd, 0x21, 0xe3, 0xd2, 0xd0, 0x23, 0x88, 0x44, + 0x03, 0x41, 0x7a, 0xe3, 0x1e, 0xc7, 0x0d, 0x7e, 0x21, 0x1c, 0x60, 0x18, + 0xda, 0x77, 0x4e, 0xae, 0x32, 0xd4, 0xab, 0xdd, 0xf0, 0x34, 0x76, 0xec, + 0x35, 0x93, 0x2d, 0x70, 0x85, 0x51, 0xc2, 0x06, 0x71, 0x2c, 0x8c, 0x4c, + 0x6a, 0x94, 0xa2, 0xa8, 0xdb, 0xa0, 0x7a, 0xa3, 0x0b, 0x9a, 0xee, 0x4b, + 0x55, 0x08, 0x07, 0xe8, 0x19, 0x53, 0x18, 0x28, 0xa7, 0x82, 0xa0, 0xaa, + 0x2e, 0x1d, 0x17, 0xd1, 0x00, 0x1f, 0x7c, 0x19, 0x07, 0x7c, 0x42, 0x40, + 0x6f, 0x02, 0x77, 0x30, 0x48, 0xe5, 0x2d, 0x81, 0x01, 0xd5, 0x02, 0x7a, + 0xf7, 0x20, 0x30, 0xbd, 0x98, 0xd7, 0x6c, 0x50, 0xda, 0x06, 0x62, 0x7d, + 0xe9, 0xf4, 0x15, 0xf0, 0x07, 0xb2, 0x7f, 0x6a, 0x9a, 0x05, 0x4a, 0x13, + 0x4f, 0x1e, 0x76, 0x39, 0x20, 0xd6, 0x77, 0x24, 0xdd, 0x7b, 0x08, 0x48, + 0x8f, 0x7f, 0xf3, 0xd4, 0x49, 0x0d, 0x83, 0xd2, 0xc4, 0xe1, 0xfb, 0xd7, + 0x87, 0x67, 0x8b, 0x16, 0xb0, 0x72, 0x7c, 0x59, 0x88, 0xfc, 0x60, 0xe3, + 0x06, 0xce, 0xd1, 0x17, 0xd2, 0x63, 0x12, 0x3b, 0xaf, 0xbf, 0x07, 0x8f, + 0x7f, 0x56, 0xb0, 0x72, 0xb3, 0x43, 0xc4, 0xce, 0xed, 0x17, 0x85, 0x88, + 0x9d, 0xe3, 0xee, 0xda, 0xb0, 0x9d, 0x37, 0xde, 0x24, 0x79, 0x3a, 0xd4, + 0x54, 0x8d, 0xae, 0xe5, 0xef, 0x6e, 0xaf, 0xbf, 0x82, 0x92, 0xe7, 0x5e, + 0x38, 0x90, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, + 0xae, 0x42, 0x60, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; +unsigned int kSampleTarLength = 10240;