From f0e491d390a4514633f1af4da4c46a7dad67ebec Mon Sep 17 00:00:00 2001 From: Andrew Lindesay Date: Tue, 28 Jul 2020 23:29:10 +1200 Subject: [PATCH] HaikuDepot: Icon / Tarball Handling Instead of exploding the tar-ball of icons from HDS, keep it as a tar-ball, index it and read data directly out from specific indicies on demand. This will speed up the process of downloading the icons by removing the unpack. Also updates will be faster by avoiding the need to delete the old icon files. Because icons are loaded on-demand, the start time is faster by avoiding all the icon loads. There are also savings on memory consumption. Indexing on each load is surprisingly fast so no external index is maintained. Likewise for the tar-balls's meta-data. This commit does not cover the implementation of an LRU cache of the icons in memory. Relates to #15370 Change-Id: Ia1647d8c805be89618f493d2592bf7877fca3f14 Reviewed-on: https://review.haiku-os.org/c/haiku/+/3205 Reviewed-by: Adrien Destugues --- src/apps/haikudepot/HaikuDepotConstants.h | 8 + src/apps/haikudepot/Jamfile | 3 +- src/apps/haikudepot/model/IconTarPtr.cpp | 48 + src/apps/haikudepot/model/IconTarPtr.h | 38 + src/apps/haikudepot/model/LocalIconStore.cpp | 97 -- src/apps/haikudepot/model/LocalIconStore.h | 33 - src/apps/haikudepot/model/Model.cpp | 22 +- src/apps/haikudepot/model/Model.h | 9 +- .../haikudepot/model/PackageIconRepository.h | 22 + .../model/PackageIconTarRepository.cpp | 371 ++++++++ .../model/PackageIconTarRepository.h | 65 ++ src/apps/haikudepot/model/PackageInfo.cpp | 27 +- src/apps/haikudepot/model/PackageInfo.h | 10 +- .../server/AbstractServerProcess.cpp | 81 +- .../haikudepot/server/AbstractServerProcess.h | 22 +- .../server/ServerIconExportUpdateProcess.cpp | 408 ++++---- .../server/ServerIconExportUpdateProcess.h | 40 +- .../haikudepot/server/StandardMetaData.cpp | 14 +- src/apps/haikudepot/server/StandardMetaData.h | 17 +- src/apps/haikudepot/tar/TarArchiveHeader.cpp | 147 +-- src/apps/haikudepot/tar/TarArchiveHeader.h | 29 +- src/apps/haikudepot/tar/TarArchiveService.cpp | 295 +++--- src/apps/haikudepot/tar/TarArchiveService.h | 41 +- src/apps/haikudepot/ui/App.cpp | 3 +- .../haikudepot/ui/FeaturedPackagesView.cpp | 41 +- src/apps/haikudepot/ui/FeaturedPackagesView.h | 7 +- src/apps/haikudepot/ui/MainWindow.cpp | 8 +- src/apps/haikudepot/ui/PackageInfoView.cpp | 31 +- src/apps/haikudepot/ui/PackageInfoView.h | 6 +- src/apps/haikudepot/ui/PackageListView.cpp | 163 ++-- src/apps/haikudepot/ui/PackageListView.h | 6 +- src/apps/haikudepot/ui/RatePackageWindow.cpp | 4 +- src/apps/haikudepot/ui/ScreenshotWindow.cpp | 4 +- src/apps/haikudepot/ui_generic/BitmapView.cpp | 2 +- src/apps/haikudepot/ui_generic/BitmapView.h | 5 +- src/apps/haikudepot/ui_generic/RatingView.cpp | 4 +- .../haikudepot/ui_generic/SharedBitmap.cpp | 80 +- src/apps/haikudepot/ui_generic/SharedBitmap.h | 15 +- src/apps/haikudepot/util/DataIOUtils.cpp | 74 +- src/apps/haikudepot/util/DataIOUtils.h | 29 +- src/apps/haikudepot/util/RatingUtils.cpp | 4 +- src/apps/haikudepot/util/StorageUtils.cpp | 28 + src/apps/haikudepot/util/StorageUtils.h | 6 + .../apps/haikudepot/HaikuDepotTestAddon.cpp | 6 +- src/tests/apps/haikudepot/Jamfile | 10 + .../apps/haikudepot/StorageUtilsTest.cpp | 50 + src/tests/apps/haikudepot/StorageUtilsTest.h | 23 + .../apps/haikudepot/TarArchiveServiceTest.cpp | 102 ++ .../apps/haikudepot/TarArchiveServiceTest.h | 28 + .../haikudepot/TarArchiveServiceTestSample.h | 876 ++++++++++++++++++ 50 files changed, 2513 insertions(+), 949 deletions(-) create mode 100644 src/apps/haikudepot/model/IconTarPtr.cpp create mode 100644 src/apps/haikudepot/model/IconTarPtr.h delete mode 100644 src/apps/haikudepot/model/LocalIconStore.cpp delete mode 100644 src/apps/haikudepot/model/LocalIconStore.h create mode 100644 src/apps/haikudepot/model/PackageIconRepository.h create mode 100644 src/apps/haikudepot/model/PackageIconTarRepository.cpp create mode 100644 src/apps/haikudepot/model/PackageIconTarRepository.h create mode 100644 src/tests/apps/haikudepot/StorageUtilsTest.cpp create mode 100644 src/tests/apps/haikudepot/StorageUtilsTest.h create mode 100644 src/tests/apps/haikudepot/TarArchiveServiceTest.cpp create mode 100644 src/tests/apps/haikudepot/TarArchiveServiceTest.h create mode 100644 src/tests/apps/haikudepot/TarArchiveServiceTestSample.h 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;