diff --git a/.flutter b/.flutter index 4d9e56e69..84a1e904f 160000 --- a/.flutter +++ b/.flutter @@ -1 +1 @@ -Subproject commit 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf +Subproject commit 84a1e904f44f9b0e9c4510138010edcc653163f8 diff --git a/CHANGELOG.md b/CHANGELOG.md index 34eed96ef..7c35f01c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,12 @@ All notable changes to this project will be documented in this file. ### Added - option to set the Tags page as home +- support for animated PNG ### Changed - remember whether to show the title filter when picking albums +- upgraded Flutter to stable v3.10.0 ## [v1.8.6] - 2023-04-30 diff --git a/android/app/build.gradle b/android/app/build.gradle index 7f038fe1a..bf19c8257 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,3 +1,5 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + plugins { id 'com.android.application' id 'kotlin-android' @@ -46,7 +48,7 @@ if (keystorePropertiesFile.exists()) { android { namespace 'deckers.thibault.aves' - compileSdkVersion 33 + compileSdk 33 ndkVersion flutter.ndkVersion compileOptions { @@ -60,10 +62,6 @@ android { disable 'InvalidPackage' } - kotlinOptions { - jvmTarget = '1.8' - } - sourceSets { main.java.srcDirs += 'src/main/kotlin' } @@ -174,6 +172,15 @@ android { } } +tasks.withType(KotlinCompile).configureEach { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + + kotlinOptions { + jvmTarget = '1.8' + } +} + flutter { source '../..' } @@ -195,10 +202,10 @@ repositories { } dependencies { - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1' implementation "androidx.appcompat:appcompat:1.6.1" - implementation 'androidx.core:core-ktx:1.10.0' + implementation 'androidx.core:core-ktx:1.10.1' implementation 'androidx.exifinterface:exifinterface:1.3.6' implementation 'androidx.lifecycle:lifecycle-process:2.6.1' implementation 'androidx.media:media:1.6.0' diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 09b752faf..eec888a79 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -8,7 +8,6 @@ This change eventually prevents building the app with Flutter v3.7.11. --> { } @override - ImageStreamCompleter loadBuffer(AppIconImageKey key, DecoderBufferCallback decode) { + ImageStreamCompleter loadImage(AppIconImageKey key, ImageDecoderCallback decode) { return MultiFrameImageStreamCompleter( codec: _loadAsync(key, decode), scale: key.scale, @@ -37,7 +37,7 @@ class AppIconImage extends ImageProvider { ); } - Future _loadAsync(AppIconImageKey key, DecoderBufferCallback decode) async { + Future _loadAsync(AppIconImageKey key, ImageDecoderCallback decode) async { try { final bytes = await appService.getAppIcon(key.packageName, key.size); final buffer = await ui.ImmutableBuffer.fromUint8List(bytes.isEmpty ? kTransparentImage : bytes); diff --git a/lib/image_providers/region_provider.dart b/lib/image_providers/region_provider.dart index 135215324..dc85ccaa1 100644 --- a/lib/image_providers/region_provider.dart +++ b/lib/image_providers/region_provider.dart @@ -18,7 +18,7 @@ class RegionProvider extends ImageProvider { } @override - ImageStreamCompleter loadBuffer(RegionProviderKey key, DecoderBufferCallback decode) { + ImageStreamCompleter loadImage(RegionProviderKey key, ImageDecoderCallback decode) { return MultiFrameImageStreamCompleter( codec: _loadAsync(key, decode), scale: 1.0, @@ -28,7 +28,7 @@ class RegionProvider extends ImageProvider { ); } - Future _loadAsync(RegionProviderKey key, DecoderBufferCallback decode) async { + Future _loadAsync(RegionProviderKey key, ImageDecoderCallback decode) async { final uri = key.uri; final mimeType = key.mimeType; final pageId = key.pageId; diff --git a/lib/image_providers/thumbnail_provider.dart b/lib/image_providers/thumbnail_provider.dart index bf13c32bc..33f05fec4 100644 --- a/lib/image_providers/thumbnail_provider.dart +++ b/lib/image_providers/thumbnail_provider.dart @@ -19,7 +19,7 @@ class ThumbnailProvider extends ImageProvider { } @override - ImageStreamCompleter loadBuffer(ThumbnailProviderKey key, DecoderBufferCallback decode) { + ImageStreamCompleter loadImage(ThumbnailProviderKey key, ImageDecoderCallback decode) { return MultiFrameImageStreamCompleter( codec: _loadAsync(key, decode), scale: 1.0, @@ -30,7 +30,7 @@ class ThumbnailProvider extends ImageProvider { ); } - Future _loadAsync(ThumbnailProviderKey key, DecoderBufferCallback decode) async { + Future _loadAsync(ThumbnailProviderKey key, ImageDecoderCallback decode) async { final uri = key.uri; final mimeType = key.mimeType; final pageId = key.pageId; diff --git a/lib/image_providers/uri_image_provider.dart b/lib/image_providers/uri_image_provider.dart index ffcb622f7..38842e742 100644 --- a/lib/image_providers/uri_image_provider.dart +++ b/lib/image_providers/uri_image_provider.dart @@ -32,7 +32,7 @@ class UriImage extends ImageProvider with EquatableMixin { } @override - ImageStreamCompleter loadBuffer(UriImage key, DecoderBufferCallback decode) { + ImageStreamCompleter loadImage(UriImage key, ImageDecoderCallback decode) { final chunkEvents = StreamController(); return MultiFrameImageStreamCompleter( @@ -45,7 +45,7 @@ class UriImage extends ImageProvider with EquatableMixin { ); } - Future _loadAsync(UriImage key, DecoderBufferCallback decode, StreamController chunkEvents) async { + Future _loadAsync(UriImage key, ImageDecoderCallback decode, StreamController chunkEvents) async { assert(key == this); try { diff --git a/lib/model/db/db_metadata_sqflite_upgrade.dart b/lib/model/db/db_metadata_sqflite_upgrade.dart index f2840e2ee..ef0b66074 100644 --- a/lib/model/db/db_metadata_sqflite_upgrade.dart +++ b/lib/model/db/db_metadata_sqflite_upgrade.dart @@ -21,34 +21,24 @@ class MetadataDbUpgrader { switch (oldVersion) { case 1: await _upgradeFrom1(db); - break; case 2: await _upgradeFrom2(db); - break; case 3: await _upgradeFrom3(db); - break; case 4: await _upgradeFrom4(db); - break; case 5: await _upgradeFrom5(db); - break; case 6: await _upgradeFrom6(db); - break; case 7: await _upgradeFrom7(db); - break; case 8: await _upgradeFrom8(db); - break; case 9: await _upgradeFrom9(db); - break; case 10: await _upgradeFrom10(db); - break; } oldVersion++; } diff --git a/lib/model/entry/extensions/catalog.dart b/lib/model/entry/extensions/catalog.dart index 6a7aa331e..73ee03ac6 100644 --- a/lib/model/entry/extensions/catalog.dart +++ b/lib/model/entry/extensions/catalog.dart @@ -39,7 +39,7 @@ extension ExtraAvesEntryCatalog on AvesEntry { if (isGeotiff && !hasGps) { final info = await metadataFetchService.getGeoTiffInfo(this); if (info != null) { - final center = MappedGeoTiff( + final center = GeoTiffCoordinateConverter( info: info, entry: this, ).center; diff --git a/lib/model/entry/extensions/metadata_edition.dart b/lib/model/entry/extensions/metadata_edition.dart index 9d1f2f7c3..c28c26d8d 100644 --- a/lib/model/entry/extensions/metadata_edition.dart +++ b/lib/model/entry/extensions/metadata_edition.dart @@ -52,7 +52,6 @@ extension ExtraAvesEntryMetadataEdition on AvesEntry { case DateEditAction.copyItem: case DateEditAction.extractFromTitle: editCreateDateXmp(descriptions, appliedModifier.setDateTime); - break; case DateEditAction.shift: final xmpDate = XMP.getString(descriptions, XmpAttributes.xmpCreateDate, namespace: XmpNamespaces.xmp); if (xmpDate != null) { @@ -65,10 +64,8 @@ extension ExtraAvesEntryMetadataEdition on AvesEntry { reportService.recordError('failed to parse XMP date=$xmpDate', null); } } - break; case DateEditAction.remove: editCreateDateXmp(descriptions, null); - break; } return true; }), @@ -541,10 +538,8 @@ extension ExtraAvesEntryMetadataEdition on AvesEntry { } } } on FileSystemException catch (_) {} - break; default: date = await metadataFetchService.getDate(this, source.toMetadataField()!); - break; } } return date != null ? DateModifier.setCustom(mainMetadataDate(), date) : null; diff --git a/lib/model/filters/album.dart b/lib/model/filters/album.dart index 7f5f8aa77..34e4ab626 100644 --- a/lib/model/filters/album.dart +++ b/lib/model/filters/album.dart @@ -78,7 +78,6 @@ class AlbumFilter extends CoveredCollectionFilter { case AlbumType.app: final appColor = colors.appColor(album); if (appColor != null) return appColor; - break; case AlbumType.camera: return SynchronousFuture(colors.albumCamera); case AlbumType.download: diff --git a/lib/model/filters/aspect_ratio.dart b/lib/model/filters/aspect_ratio.dart index 92ee97279..f21f48280 100644 --- a/lib/model/filters/aspect_ratio.dart +++ b/lib/model/filters/aspect_ratio.dart @@ -21,13 +21,10 @@ class AspectRatioFilter extends CollectionFilter { switch (op) { case QueryFilter.opEqual: _test = (entry) => entry.displayAspectRatio == threshold; - break; case QueryFilter.opLower: _test = (entry) => entry.displayAspectRatio < threshold; - break; case QueryFilter.opGreater: _test = (entry) => entry.displayAspectRatio > threshold; - break; } } diff --git a/lib/model/filters/date.dart b/lib/model/filters/date.dart index 3c82ae15c..4749fcd4d 100644 --- a/lib/model/filters/date.dart +++ b/lib/model/filters/date.dart @@ -25,13 +25,10 @@ class DateFilter extends CollectionFilter { switch (level) { case DateLevel.y: _test = (entry) => entry.bestDate?.isAtSameYearAs(_effectiveDate) ?? false; - break; case DateLevel.ym: _test = (entry) => entry.bestDate?.isAtSameMonthAs(_effectiveDate) ?? false; - break; case DateLevel.ymd: _test = (entry) => entry.bestDate?.isAtSameDayAs(_effectiveDate) ?? false; - break; case DateLevel.md: final month = _effectiveDate.month; final day = _effectiveDate.day; @@ -39,15 +36,12 @@ class DateFilter extends CollectionFilter { final bestDate = entry.bestDate; return bestDate != null && bestDate.month == month && bestDate.day == day; }; - break; case DateLevel.m: final month = _effectiveDate.month; _test = (entry) => entry.bestDate?.month == month; - break; case DateLevel.d: final day = _effectiveDate.day; _test = (entry) => entry.bestDate?.day == day; - break; } } diff --git a/lib/model/filters/location.dart b/lib/model/filters/location.dart index 0c547defd..3a22391b5 100644 --- a/lib/model/filters/location.dart +++ b/lib/model/filters/location.dart @@ -29,13 +29,10 @@ class LocationFilter extends CoveredCollectionFilter { switch (level) { case LocationLevel.country: _test = (entry) => entry.addressDetails?.countryCode == _code; - break; case LocationLevel.state: _test = (entry) => entry.addressDetails?.stateCode == _code; - break; case LocationLevel.place: _test = (entry) => entry.addressDetails?.place == _location; - break; } } } @@ -57,7 +54,6 @@ class LocationFilter extends CoveredCollectionFilter { if (_code != null) { location = _nameAndCode; } - break; case LocationLevel.place: break; } diff --git a/lib/model/filters/missing.dart b/lib/model/filters/missing.dart index 6060d3f36..9128d1eb1 100644 --- a/lib/model/filters/missing.dart +++ b/lib/model/filters/missing.dart @@ -26,15 +26,12 @@ class MissingFilter extends CollectionFilter { case _date: _test = (entry) => (entry.catalogMetadata?.dateMillis ?? 0) == 0; _icon = AIcons.dateUndated; - break; case _fineAddress: _test = (entry) => entry.hasGps && !entry.hasFineAddress; _icon = AIcons.locationUnlocated; - break; case _title: _test = (entry) => (entry.catalogMetadata?.xmpTitle ?? '').isEmpty; _icon = AIcons.descriptionUntitled; - break; } } diff --git a/lib/model/filters/placeholder.dart b/lib/model/filters/placeholder.dart index 32f22b1f4..bbeab3bed 100644 --- a/lib/model/filters/placeholder.dart +++ b/lib/model/filters/placeholder.dart @@ -28,13 +28,10 @@ class PlaceholderFilter extends CollectionFilter { switch (placeholder) { case _country: _icon = AIcons.country; - break; case _state: _icon = AIcons.state; - break; case _place: _icon = AIcons.place; - break; } } @@ -74,7 +71,6 @@ class PlaceholderFilter extends CollectionFilter { case _place: return address.place; } - break; } return null; } diff --git a/lib/model/filters/query.dart b/lib/model/filters/query.dart index 523a1b156..2c1689551 100644 --- a/lib/model/filters/query.dart +++ b/lib/model/filters/query.dart @@ -117,7 +117,6 @@ class QueryFilter extends CollectionFilter { if (op == opEqual) { return (entry) => entry.contentId == valueInt; } - break; case keyContentYear: if (valueInt == null) return null; switch (op) { @@ -128,7 +127,6 @@ class QueryFilter extends CollectionFilter { case opGreater: return (entry) => (entry.bestDate?.year ?? 0) > valueInt; } - break; case keyContentMonth: if (valueInt == null) return null; switch (op) { @@ -139,7 +137,6 @@ class QueryFilter extends CollectionFilter { case opGreater: return (entry) => (entry.bestDate?.month ?? 0) > valueInt; } - break; case keyContentDay: if (valueInt == null) return null; switch (op) { @@ -150,7 +147,6 @@ class QueryFilter extends CollectionFilter { case opGreater: return (entry) => (entry.bestDate?.day ?? 0) > valueInt; } - break; case keyContentWidth: if (valueInt == null) return null; switch (op) { @@ -161,7 +157,6 @@ class QueryFilter extends CollectionFilter { case opGreater: return (entry) => entry.displaySize.width > valueInt; } - break; case keyContentHeight: if (valueInt == null) return null; switch (op) { @@ -172,7 +167,6 @@ class QueryFilter extends CollectionFilter { case opGreater: return (entry) => entry.displaySize.height > valueInt; } - break; case keyContentSize: match = _fileSizePattern.firstMatch(valueString); if (match == null) return null; @@ -187,13 +181,10 @@ class QueryFilter extends CollectionFilter { switch (multiplierString) { case 'K': bytes *= kilo; - break; case 'M': bytes *= mega; - break; case 'G': bytes *= giga; - break; } switch (op) { @@ -204,7 +195,6 @@ class QueryFilter extends CollectionFilter { case opGreater: return (entry) => (entry.sizeBytes ?? 0) > bytes; } - break; } return null; diff --git a/lib/model/filters/type.dart b/lib/model/filters/type.dart index 9eafa93b3..b94dcb6ac 100644 --- a/lib/model/filters/type.dart +++ b/lib/model/filters/type.dart @@ -37,27 +37,21 @@ class TypeFilter extends CollectionFilter { case _animated: _test = (entry) => entry.isAnimated; _icon = AIcons.animated; - break; case _geotiff: _test = (entry) => entry.isGeotiff; _icon = AIcons.geo; - break; case _motionPhoto: _test = (entry) => entry.isMotionPhoto; _icon = AIcons.motionPhoto; - break; case _panorama: _test = (entry) => entry.isImage && entry.is360; _icon = AIcons.panorama; - break; case _raw: _test = (entry) => entry.isRaw; _icon = AIcons.raw; - break; case _sphericalVideo: _test = (entry) => entry.isVideo && entry.is360; _icon = AIcons.sphericalVideo; - break; } } diff --git a/lib/model/geotiff.dart b/lib/model/geotiff.dart index efa6afb46..d549cd29d 100644 --- a/lib/model/geotiff.dart +++ b/lib/model/geotiff.dart @@ -8,8 +8,7 @@ import 'package:aves/ref/metadata/geotiff.dart'; import 'package:aves/utils/math_utils.dart'; import 'package:aves_map/aves_map.dart'; import 'package:equatable/equatable.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/painting.dart'; +import 'package:flutter/widgets.dart'; import 'package:latlong2/latlong.dart'; import 'package:proj4dart/proj4dart.dart' as proj4; @@ -42,19 +41,145 @@ class GeoTiffInfo extends Equatable { class MappedGeoTiff with MapOverlay { final AvesEntry entry; - late LatLng? Function(Point pixel) pointToLatLng; - late Point? Function(Point smPoint) epsg3857ToPoint; - static final mapServiceTileSize = (256 * ui.window.devicePixelRatio).round(); - static final mapServiceHelper = MapServiceHelper(mapServiceTileSize); - static final tileImagePaint = Paint(); - static final tileMissingPaint = Paint() + late final GeoTiffCoordinateConverter _converter; + late final int _mapServiceTileSize; + late final MapServiceHelper _mapServiceHelper; + + static final _tileImagePaint = Paint(); + static final _tileMissingPaint = Paint() ..style = PaintingStyle.fill ..color = const Color(0xFF000000); MappedGeoTiff({ required GeoTiffInfo info, required this.entry, + required double devicePixelRatio, + }) { + _converter = GeoTiffCoordinateConverter(info: info, entry: entry); + _mapServiceTileSize = (256 * devicePixelRatio).round(); + _mapServiceHelper = MapServiceHelper(_mapServiceTileSize); + } + + @override + Future getTile(int tx, int ty, int? zoomLevel) async { + zoomLevel ??= 0; + + // global projected coordinates in meters (EPSG:3857 Spherical Mercator) + final tileTopLeft3857 = _mapServiceHelper.tileTopLeft(tx, ty, zoomLevel); + final tileBottomRight3857 = _mapServiceHelper.tileTopLeft(tx + 1, ty + 1, zoomLevel); + + // image region coordinates in pixels + final tileTopLeftPx = _converter.epsg3857ToPoint(tileTopLeft3857); + final tileBottomRightPx = _converter.epsg3857ToPoint(tileBottomRight3857); + if (tileTopLeftPx == null || tileBottomRightPx == null) return null; + + final tileLeft = tileTopLeftPx.x; + final tileRight = tileBottomRightPx.x; + final tileTop = tileTopLeftPx.y; + final tileBottom = tileBottomRightPx.y; + + final width = entry.width; + final height = entry.height; + + final regionLeft = tileLeft.clamp(0, width); + final regionRight = tileRight.clamp(0, width); + final regionTop = tileTop.clamp(0, height); + final regionBottom = tileBottom.clamp(0, height); + + final regionWidth = regionRight - regionLeft; + final regionHeight = regionBottom - regionTop; + if (regionWidth == 0 || regionHeight == 0) return null; + + final tileXScale = (tileRight - tileLeft) / _mapServiceTileSize; + final sampleSize = max(1, highestPowerOf2(tileXScale)); + final region = entry.getRegion( + sampleSize: sampleSize, + region: Rectangle(regionLeft, regionTop, regionWidth, regionHeight), + ); + + final imageInfoCompleter = Completer(); + final imageStream = region.resolve(ImageConfiguration.empty); + final imageStreamListener = ImageStreamListener((image, synchronousCall) { + imageInfoCompleter.complete(image); + }, onError: imageInfoCompleter.completeError); + imageStream.addListener(imageStreamListener); + ImageInfo? regionImageInfo; + try { + regionImageInfo = await imageInfoCompleter.future; + } catch (error) { + debugPrint('failed to get image for region=$region with error=$error'); + } + imageStream.removeListener(imageStreamListener); + + final imageOffset = Offset( + regionLeft > tileLeft ? (regionLeft - tileLeft).toDouble() : 0, + regionTop > tileTop ? (regionTop - tileTop).toDouble() : 0, + ); + final tileImageScaleX = (tileRight - tileLeft) / _mapServiceTileSize; + final tileImageScaleY = (tileBottom - tileTop) / _mapServiceTileSize; + + final recorder = ui.PictureRecorder(); + final canvas = Canvas(recorder); + canvas.scale(1 / tileImageScaleX, 1 / tileImageScaleY); + if (regionImageInfo != null) { + final s = sampleSize.toDouble(); + canvas.scale(s, s); + canvas.drawImage(regionImageInfo.image, imageOffset / s, _tileImagePaint); + canvas.scale(1 / s, 1 / s); + } else { + // fallback to show area + canvas.drawRect( + Rect.fromLTWH( + imageOffset.dx, + imageOffset.dy, + regionWidth.toDouble(), + regionHeight.toDouble(), + ), + _tileMissingPaint, + ); + } + canvas.scale(tileImageScaleX, tileImageScaleY); + + final picture = recorder.endRecording(); + final tileImage = await picture.toImage(_mapServiceTileSize, _mapServiceTileSize); + final byteData = await tileImage.toByteData(format: ui.ImageByteFormat.png); + if (byteData == null) return null; + + return MapTile( + width: tileImage.width, + height: tileImage.height, + data: byteData.buffer.asUint8List(), + ); + } + + @override + String get id => entry.uri; + + @override + ImageProvider get imageProvider => entry.uriImage; + + @override + bool get canOverlay => center != null; + + LatLng? get center => _converter.center; + + @override + LatLng? get topLeft => _converter.topLeft; + + @override + LatLng? get bottomRight => _converter.bottomRight; +} + +class GeoTiffCoordinateConverter { + final AvesEntry entry; + + late LatLng? Function(Point pixel) pointToLatLng; + late Point? Function(Point smPoint) epsg3857ToPoint; + + GeoTiffCoordinateConverter({ + required GeoTiffInfo info, + required this.entry, }) { pointToLatLng = (_) => null; epsg3857ToPoint = (_) => null; @@ -129,113 +254,13 @@ class MappedGeoTiff with MapOverlay { }; } - @override - Future getTile(int tx, int ty, int? zoomLevel) async { - zoomLevel ??= 0; - - // global projected coordinates in meters (EPSG:3857 Spherical Mercator) - final tileTopLeft3857 = mapServiceHelper.tileTopLeft(tx, ty, zoomLevel); - final tileBottomRight3857 = mapServiceHelper.tileTopLeft(tx + 1, ty + 1, zoomLevel); - - // image region coordinates in pixels - final tileTopLeftPx = epsg3857ToPoint(tileTopLeft3857); - final tileBottomRightPx = epsg3857ToPoint(tileBottomRight3857); - if (tileTopLeftPx == null || tileBottomRightPx == null) return null; - - final tileLeft = tileTopLeftPx.x; - final tileRight = tileBottomRightPx.x; - final tileTop = tileTopLeftPx.y; - final tileBottom = tileBottomRightPx.y; - - final regionLeft = tileLeft.clamp(0, width); - final regionRight = tileRight.clamp(0, width); - final regionTop = tileTop.clamp(0, height); - final regionBottom = tileBottom.clamp(0, height); - - final regionWidth = regionRight - regionLeft; - final regionHeight = regionBottom - regionTop; - if (regionWidth == 0 || regionHeight == 0) return null; - - final tileXScale = (tileRight - tileLeft) / mapServiceTileSize; - final sampleSize = max(1, highestPowerOf2(tileXScale)); - final region = entry.getRegion( - sampleSize: sampleSize, - region: Rectangle(regionLeft, regionTop, regionWidth, regionHeight), - ); - - final imageInfoCompleter = Completer(); - final imageStream = region.resolve(ImageConfiguration.empty); - final imageStreamListener = ImageStreamListener((image, synchronousCall) { - imageInfoCompleter.complete(image); - }, onError: imageInfoCompleter.completeError); - imageStream.addListener(imageStreamListener); - ImageInfo? regionImageInfo; - try { - regionImageInfo = await imageInfoCompleter.future; - } catch (error) { - debugPrint('failed to get image for region=$region with error=$error'); - } - imageStream.removeListener(imageStreamListener); - - final imageOffset = Offset( - regionLeft > tileLeft ? (regionLeft - tileLeft).toDouble() : 0, - regionTop > tileTop ? (regionTop - tileTop).toDouble() : 0, - ); - final tileImageScaleX = (tileRight - tileLeft) / mapServiceTileSize; - final tileImageScaleY = (tileBottom - tileTop) / mapServiceTileSize; - - final recorder = ui.PictureRecorder(); - final canvas = Canvas(recorder); - canvas.scale(1 / tileImageScaleX, 1 / tileImageScaleY); - if (regionImageInfo != null) { - final s = sampleSize.toDouble(); - canvas.scale(s, s); - canvas.drawImage(regionImageInfo.image, imageOffset / s, tileImagePaint); - canvas.scale(1 / s, 1 / s); - } else { - // fallback to show area - canvas.drawRect( - Rect.fromLTWH( - imageOffset.dx, - imageOffset.dy, - regionWidth.toDouble(), - regionHeight.toDouble(), - ), - tileMissingPaint, - ); - } - canvas.scale(tileImageScaleX, tileImageScaleY); - - final picture = recorder.endRecording(); - final tileImage = await picture.toImage(mapServiceTileSize, mapServiceTileSize); - final byteData = await tileImage.toByteData(format: ui.ImageByteFormat.png); - if (byteData == null) return null; - - return MapTile( - width: tileImage.width, - height: tileImage.height, - data: byteData.buffer.asUint8List(), - ); - } - - @override - String get id => entry.uri; - - @override - ImageProvider get imageProvider => entry.uriImage; - int get width => entry.width; int get height => entry.height; - @override - bool get canOverlay => center != null; - LatLng? get center => pointToLatLng(Point((width / 2).round(), (height / 2).round())); - @override LatLng? get topLeft => pointToLatLng(const Point(0, 0)); - @override LatLng? get bottomRight => pointToLatLng(Point(width, height)); } diff --git a/lib/model/naming_pattern.dart b/lib/model/naming_pattern.dart index 2fd502291..e5eee02d4 100644 --- a/lib/model/naming_pattern.dart +++ b/lib/model/naming_pattern.dart @@ -38,10 +38,8 @@ class NamingPattern { if (processorOptions != null) { processors.add(DateNamingProcessor(processorOptions.trim())); } - break; case NameNamingProcessor.key: processors.add(const NameNamingProcessor()); - break; case CounterNamingProcessor.key: int? start, padding; _applyProcessorOptions(processorOptions, (key, value) { @@ -50,18 +48,14 @@ class NamingPattern { switch (key) { case CounterNamingProcessor.optionStart: start = valueInt; - break; case CounterNamingProcessor.optionPadding: padding = valueInt; - break; } } }); processors.add(CounterNamingProcessor(start: start ?? defaultCounterStart, padding: padding ?? defaultCounterPadding)); - break; default: debugPrint('unsupported naming processor: ${match.group(0)}'); - break; } index = end; }); diff --git a/lib/model/settings/enums/display_refresh_rate_mode.dart b/lib/model/settings/enums/display_refresh_rate_mode.dart index 942d901b9..32d1cd28b 100644 --- a/lib/model/settings/enums/display_refresh_rate_mode.dart +++ b/lib/model/settings/enums/display_refresh_rate_mode.dart @@ -15,13 +15,10 @@ extension ExtraDisplayRefreshRateMode on DisplayRefreshRateMode { switch (this) { case DisplayRefreshRateMode.auto: await FlutterDisplayMode.setPreferredMode(DisplayMode.auto); - break; case DisplayRefreshRateMode.highest: await FlutterDisplayMode.setHighRefreshRate(); - break; case DisplayRefreshRateMode.lowest: await FlutterDisplayMode.setLowRefreshRate(); - break; } } } diff --git a/lib/model/settings/settings.dart b/lib/model/settings/settings.dart index b9a0ea3c8..966b545db 100644 --- a/lib/model/settings/settings.dart +++ b/lib/model/settings/settings.dart @@ -375,7 +375,7 @@ class Settings extends ChangeNotifier { if (_locale != null) { preferredLocales.add(_locale); } else { - preferredLocales.addAll(WidgetsBinding.instance.window.locales); + preferredLocales.addAll(WidgetsBinding.instance.platformDispatcher.locales); if (preferredLocales.isEmpty) { // the `window` locales may be empty in a window-less service context preferredLocales.addAll(_systemLocalesFallback); @@ -1022,7 +1022,6 @@ class Settings extends ChangeNotifier { if (value is num) { isRotationLocked = value == 0; } - break; case platformTransitionAnimationScaleKey: if (value is num) { areAnimationsRemoved = value == 0; @@ -1080,7 +1079,6 @@ class Settings extends ChangeNotifier { } else { debugPrint('failed to import key=$key, value=$newValue is not an int'); } - break; case subtitleFontSizeKey: case infoMapZoomKey: if (newValue is double) { @@ -1088,7 +1086,6 @@ class Settings extends ChangeNotifier { } else { debugPrint('failed to import key=$key, value=$newValue is not a double'); } - break; case isInstalledAppAccessAllowedKey: case isErrorReportingAllowedKey: case enableDynamicColorKey: @@ -1144,7 +1141,6 @@ class Settings extends ChangeNotifier { } else { debugPrint('failed to import key=$key, value=$newValue is not a bool'); } - break; case localeKey: case displayRefreshRateModeKey: case themeBrightnessKey: @@ -1187,7 +1183,6 @@ class Settings extends ChangeNotifier { } else { debugPrint('failed to import key=$key, value=$newValue is not a string'); } - break; case drawerTypeBookmarksKey: case drawerAlbumBookmarksKey: case drawerPageBookmarksKey: @@ -1203,7 +1198,6 @@ class Settings extends ChangeNotifier { } else { debugPrint('failed to import key=$key, value=$newValue is not a list'); } - break; } } if (oldValue != newValue) { diff --git a/lib/model/source/album.dart b/lib/model/source/album.dart index e4524418e..809086b97 100644 --- a/lib/model/source/album.dart +++ b/lib/model/source/album.dart @@ -47,13 +47,10 @@ mixin AlbumMixin on SourceBase { switch (androidFileUtils.getAlbumType(album)) { case AlbumType.regular: regularAlbums.add(album); - break; case AlbumType.app: appAlbums.add(album); - break; default: specialAlbums.add(album); - break; } } return Map.fromEntries([...specialAlbums, ...appAlbums, ...regularAlbums].map((album) => MapEntry( diff --git a/lib/model/source/collection_lens.dart b/lib/model/source/collection_lens.dart index 44b37f548..dfc922a3b 100644 --- a/lib/model/source/collection_lens.dart +++ b/lib/model/source/collection_lens.dart @@ -68,10 +68,8 @@ class CollectionLens with ChangeNotifier { case MoveType.move: case MoveType.fromBin: refresh(); - break; case MoveType.toBin: _onEntryRemoved(e.entries); - break; } })); _subscriptions.add(sourceEvents.on().listen((e) => refresh())); @@ -213,16 +211,12 @@ class CollectionLens with ChangeNotifier { switch (sortFactor) { case EntrySortFactor.date: _filteredSortedEntries.sort(AvesEntrySort.compareByDate); - break; case EntrySortFactor.name: _filteredSortedEntries.sort(AvesEntrySort.compareByName); - break; case EntrySortFactor.rating: _filteredSortedEntries.sort(AvesEntrySort.compareByRating); - break; case EntrySortFactor.size: _filteredSortedEntries.sort(AvesEntrySort.compareBySize); - break; } if (sortReverse) { _filteredSortedEntries = _filteredSortedEntries.reversed.toList(); @@ -240,33 +234,25 @@ class CollectionLens with ChangeNotifier { switch (sectionFactor) { case EntryGroupFactor.album: sections = groupBy(_filteredSortedEntries, (entry) => EntryAlbumSectionKey(entry.directory)); - break; case EntryGroupFactor.month: sections = groupBy(_filteredSortedEntries, (entry) => EntryDateSectionKey(entry.monthTaken)); - break; case EntryGroupFactor.day: sections = groupBy(_filteredSortedEntries, (entry) => EntryDateSectionKey(entry.dayTaken)); - break; case EntryGroupFactor.none: sections = Map.fromEntries([ MapEntry(const SectionKey(), _filteredSortedEntries), ]); - break; } - break; case EntrySortFactor.name: final byAlbum = groupBy(_filteredSortedEntries, (entry) => EntryAlbumSectionKey(entry.directory)); final compare = sortReverse ? (a, b) => source.compareAlbumsByName(b.directory!, a.directory!) : (a, b) => source.compareAlbumsByName(a.directory!, b.directory!); sections = SplayTreeMap>.of(byAlbum, compare); - break; case EntrySortFactor.rating: sections = groupBy(_filteredSortedEntries, (entry) => EntryRatingSectionKey(entry.rating)); - break; case EntrySortFactor.size: sections = Map.fromEntries([ MapEntry(const SectionKey(), _filteredSortedEntries), ]); - break; } } sections = Map.unmodifiable(sections); diff --git a/lib/model/source/collection_source.dart b/lib/model/source/collection_source.dart index ec7d49304..5464e6774 100644 --- a/lib/model/source/collection_source.dart +++ b/lib/model/source/collection_source.dart @@ -221,18 +221,14 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place switch (key) { case 'contentId': entry.contentId = newValue as int?; - break; case 'dateModifiedSecs': // `dateModifiedSecs` changes when moving entries to another directory, // but it does not change when renaming the containing directory entry.dateModifiedSecs = newValue as int?; - break; case 'path': entry.path = newValue as String?; - break; case 'title': entry.sourceTitle = newValue as String?; - break; case 'trashed': final trashed = newValue as bool; entry.trashed = trashed; @@ -243,13 +239,10 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place dateMillis: DateTime.now().millisecondsSinceEpoch, ) : null; - break; case 'uri': entry.uri = newValue as String; - break; case 'origin': entry.origin = newValue as int; - break; } }); if (entry.trashed) { @@ -371,16 +364,13 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place switch (moveType) { case MoveType.copy: addEntries(movedEntries); - break; case MoveType.move: case MoveType.export: cleanEmptyAlbums(fromAlbums.whereNotNull().toSet()); addDirectories(albums: destinationAlbums); - break; case MoveType.toBin: case MoveType.fromBin: updateDerivedFilters(movedEntries); - break; } invalidateAlbumFilterSummary(directories: fromAlbums); _invalidate(entries: movedEntries); diff --git a/lib/model/video/metadata.dart b/lib/model/video/metadata.dart index eab4c5031..25593eb39 100644 --- a/lib/model/video/metadata.dart +++ b/lib/model/video/metadata.dart @@ -210,38 +210,29 @@ class VideoMetadataFormatter { case Keys.androidCaptureFramerate: final captureFps = double.parse(value); save('Capture Frame Rate', '${roundToPrecision(captureFps, decimals: 3).toString()} FPS'); - break; case Keys.androidManufacturer: save('Android Manufacturer', value); - break; case Keys.androidModel: save('Android Model', value); - break; case Keys.androidVersion: save('Android Version', value); - break; case Keys.bitrate: case Keys.bps: save('Bit Rate', _formatMetric(value, 'b/s')); - break; case Keys.byteCount: save('Size', _formatFilesize(value)); - break; case Keys.channelLayout: save('Channel Layout', _formatChannelLayout(value)); - break; case Keys.codecName: if (value != 'none') { save('Format', _formatCodecName(value)); } - break; case Keys.codecPixelFormat: if (streamType == MediaStreamTypes.video) { // this is just a short name used by FFmpeg // user-friendly descriptions for related enums are defined in libavutil/pixfmt.h save('Pixel Format', (value as String).toUpperCase()); } - break; case Keys.codecProfileId: { final profile = int.tryParse(value); @@ -260,18 +251,14 @@ class VideoMetadataFormatter { profileString = Hevc.formatProfile(profile, level); } } - break; } case Codecs.aac: profileString = AAC.formatProfile(profile); - break; default: profileString = profile.toString(); - break; } save('Format Profile', profileString); } - break; } case Keys.compatibleBrands: final formattedBrands = RegExp(r'.{4}').allMatches(value).map((m) { @@ -279,52 +266,37 @@ class VideoMetadataFormatter { return _formatBrand(brand); }).join(', '); save('Compatible Brands', formattedBrands); - break; case Keys.creationTime: save('Creation Time', _formatDate(value)); - break; case Keys.date: if (value is String && value != '0') { final charCount = value.length; save(charCount == 4 ? 'Year' : 'Date', value); } - break; case Keys.duration: save('Duration', _formatDuration(value)); - break; case Keys.durationMicros: if (value != 0) save('Duration', formatPreciseDuration(Duration(microseconds: value))); - break; case Keys.fpsDen: save('Frame Rate', '${roundToPrecision(info[Keys.fpsNum] / info[Keys.fpsDen], decimals: 3).toString()} FPS'); - break; case Keys.frameCount: save('Frame Count', value); - break; case Keys.height: save('Height', '$value pixels'); - break; case Keys.language: if (value != 'und') save('Language', _formatLanguage(value)); - break; case Keys.location: save('Location', _formatLocation(value)); - break; case Keys.majorBrand: save('Major Brand', _formatBrand(value)); - break; case Keys.mediaFormat: save('Format', (value as String).splitMapJoin(',', onMatch: (s) => ', ', onNonMatch: _formatCodecName)); - break; case Keys.mediaType: save('Media Type', value); - break; case Keys.minorVersion: if (value != '0') save('Minor Version', value); - break; case Keys.quicktimeLocationAccuracyHorizontal: save('QuickTime Location Horizontal Accuracy', value); - break; case Keys.quicktimeCreationDate: case Keys.quicktimeLocationIso6709: case Keys.quicktimeMake: @@ -334,37 +306,27 @@ class VideoMetadataFormatter { break; case Keys.rotate: save('Rotation', '$value°'); - break; case Keys.sampleRate: save('Sample Rate', _formatMetric(value, 'Hz')); - break; case Keys.sarDen: final sarNum = info[Keys.sarNum]; final sarDen = info[Keys.sarDen]; // skip common square pixels (1:1) if (sarNum != sarDen) save('SAR', '$sarNum:$sarDen'); - break; case Keys.sourceOshash: save('Source OSHash', value); - break; case Keys.startMicros: if (value != 0) save('Start', formatPreciseDuration(Duration(microseconds: value))); - break; case Keys.statisticsWritingApp: save('Stats Writing App', value); - break; case Keys.statisticsWritingDateUtc: save('Stats Writing Date', _formatDate(value)); - break; case Keys.track: if (value != '0') save('Track', value); - break; case Keys.width: save('Width', '$value pixels'); - break; case Keys.xiaomiSlowMoment: save('Xiaomi Slow Moment', value); - break; default: save(key.toSentenceCase(), value.toString()); } diff --git a/lib/model/video/profiles/h264.dart b/lib/model/video/profiles/h264.dart index c9fe1dae6..5fb99ace3 100644 --- a/lib/model/video/profiles/h264.dart +++ b/lib/model/video/profiles/h264.dart @@ -28,43 +28,30 @@ class H264 { switch (profileIndex) { case profileBaseline: profile = 'Baseline'; - break; case profileConstrainedBaseline: profile = 'Constrained Baseline'; - break; case profileMain: profile = 'Main'; - break; case profileExtended: profile = 'Extended'; - break; case profileHigh: profile = 'High'; - break; case profileHigh10: profile = 'High 10'; - break; case profileHigh10Intra: profile = 'High 10 Intra'; - break; case profileHigh422: profile = 'High 4:2:2'; - break; case profileHigh422Intra: profile = 'High 4:2:2 Intra'; - break; case profileHigh444: profile = 'High 4:4:4'; - break; case profileHigh444Predictive: profile = 'High 4:4:4 Predictive'; - break; case profileHigh444Intra: profile = 'High 4:4:4 Intra'; - break; case profileCAVLC444: profile = 'CAVLC 4:4:4'; - break; default: return '$profileIndex'; } diff --git a/lib/model/video/profiles/hevc.dart b/lib/model/video/profiles/hevc.dart index 4678fa699..880982eef 100644 --- a/lib/model/video/profiles/hevc.dart +++ b/lib/model/video/profiles/hevc.dart @@ -9,16 +9,12 @@ class Hevc { switch (profileIndex) { case profileMain: profile = 'Main'; - break; case profileMain10: profile = 'Main 10'; - break; case profileMainStillPicture: profile = 'Main Still Picture'; - break; case profileRExt: profile = 'Format Range'; - break; default: return '$profileIndex'; } diff --git a/lib/ref/poi.dart b/lib/ref/poi.dart index 1aebe9aae..951239d39 100644 --- a/lib/ref/poi.dart +++ b/lib/ref/poi.dart @@ -12,4 +12,4 @@ class PointsOfInterest { LatLng(37.637861, 21.63), LatLng(37.949722, 27.363889), ]; -} \ No newline at end of file +} diff --git a/lib/services/analysis_service.dart b/lib/services/analysis_service.dart index 50fea1499..9c656db62 100644 --- a/lib/services/analysis_service.dart +++ b/lib/services/analysis_service.dart @@ -145,11 +145,9 @@ class Analyzer { case AnalyzerState.stopping: await _stopPlatformService(); _serviceStateNotifier.value = AnalyzerState.stopped; - break; case AnalyzerState.stopped: _controller?.stopSignal.value = true; _stopUpdateTimer(); - break; } } diff --git a/lib/services/media/media_session_service.dart b/lib/services/media/media_session_service.dart index 8280965c7..2d287f910 100644 --- a/lib/services/media/media_session_service.dart +++ b/lib/services/media/media_session_service.dart @@ -96,25 +96,19 @@ class PlatformMediaSessionService implements MediaSessionService, Disposable { switch (command) { case 'play': event = const MediaCommandEvent(MediaCommand.play); - break; case 'pause': event = const MediaCommandEvent(MediaCommand.pause); - break; case 'skip_to_next': event = const MediaCommandEvent(MediaCommand.skipToNext); - break; case 'skip_to_previous': event = const MediaCommandEvent(MediaCommand.skipToPrevious); - break; case 'stop': event = const MediaCommandEvent(MediaCommand.stop); - break; case 'seek': final position = fields['position'] as int?; if (position != null) { event = MediaSeekCommandEvent(MediaCommand.stop, position: position); } - break; } if (event != null) { _streamController.add(event); diff --git a/lib/services/metadata/svg_metadata_service.dart b/lib/services/metadata/svg_metadata_service.dart index 677bb97dc..0567b1908 100644 --- a/lib/services/metadata/svg_metadata_service.dart +++ b/lib/services/metadata/svg_metadata_service.dart @@ -80,7 +80,7 @@ class SvgMetadataService { final docDir = Map.fromEntries([ ...root.attributes.where((a) => _attributes.contains(a.name.qualified)).map((a) => MapEntry(formatKey(a.name.qualified), a.value)), ..._textElements.map((name) { - final value = root.getElement(name)?.text; + final value = root.getElement(name)?.innerText; return value != null ? MapEntry(formatKey(name), value) : null; }).whereNotNull(), ]); diff --git a/lib/services/window_service.dart b/lib/services/window_service.dart index 81796bff8..44f9a9d73 100644 --- a/lib/services/window_service.dart +++ b/lib/services/window_service.dart @@ -74,15 +74,12 @@ class PlatformWindowService implements WindowService { case Orientation.landscape: // SCREEN_ORIENTATION_SENSOR_LANDSCAPE orientationCode = 6; - break; case Orientation.portrait: // SCREEN_ORIENTATION_SENSOR_PORTRAIT orientationCode = 7; - break; default: // SCREEN_ORIENTATION_UNSPECIFIED orientationCode = -1; - break; } try { await _platform.invokeMethod('requestOrientation', { diff --git a/lib/theme/text.dart b/lib/theme/text.dart index b7840d33f..27501eb06 100644 --- a/lib/theme/text.dart +++ b/lib/theme/text.dart @@ -4,4 +4,4 @@ class AText { static const separator = ' ${UniChars.bullet} '; static const resolutionSeparator = ' ${UniChars.multiplicationSign} '; static const valueNotAvailable = UniChars.emDash; -} \ No newline at end of file +} diff --git a/lib/utils/diff_match.dart b/lib/utils/diff_match.dart index 29042c4de..4c4a7b9ef 100644 --- a/lib/utils/diff_match.dart +++ b/lib/utils/diff_match.dart @@ -274,11 +274,9 @@ class DiffMatchPatch { case Operation.insert: count_insert++; text_insert.write(diffs[pointer].text); - break; case Operation.delete: count_delete++; text_delete.write(diffs[pointer].text); - break; case Operation.equal: // Upon reaching an equality, check for prior redundancies. if (count_delete >= 1 && count_insert >= 1) { @@ -295,7 +293,6 @@ class DiffMatchPatch { count_delete = 0; text_delete.clear(); text_insert.clear(); - break; } pointer++; } @@ -1013,12 +1010,10 @@ class DiffMatchPatch { count_insert++; text_insert += diffs[pointer].text; pointer++; - break; case Operation.delete: count_delete++; text_delete += diffs[pointer].text; pointer++; - break; case Operation.equal: // Upon reaching an equality, check for prior redundancies. if (count_delete + count_insert > 1) { @@ -1068,7 +1063,6 @@ class DiffMatchPatch { count_delete = 0; text_delete = ''; text_insert = ''; - break; } } if (diffs.last.text.isEmpty) { @@ -1155,17 +1149,14 @@ class DiffMatchPatch { html.write(''); html.write(text); html.write(''); - break; case Operation.delete: html.write(''); html.write(text); html.write(''); - break; case Operation.equal: html.write(''); html.write(text); html.write(''); - break; } } return html.toString(); @@ -1209,16 +1200,13 @@ class DiffMatchPatch { switch (aDiff.operation) { case Operation.insert: insertions += aDiff.text.length; - break; case Operation.delete: deletions += aDiff.text.length; - break; case Operation.equal: // A deletion and an insertion is one substitution. levenshtein += max(insertions, deletions); insertions = 0; deletions = 0; - break; } } levenshtein += max(insertions, deletions); @@ -1239,17 +1227,14 @@ class DiffMatchPatch { text.write('+'); text.write(Uri.encodeFull(aDiff.text)); text.write('\t'); - break; case Operation.delete: text.write('-'); text.write(aDiff.text.length); text.write('\t'); - break; case Operation.equal: text.write('='); text.write(aDiff.text.length); text.write('\t'); - break; } } String delta = text.toString(); @@ -1289,7 +1274,6 @@ class DiffMatchPatch { throw ArgumentError('Illegal escape in diff_fromDelta: $param'); } diffs.add(Diff(Operation.insert, param)); - break; case '-': // Fall through. case '=': @@ -1314,7 +1298,6 @@ class DiffMatchPatch { } else { diffs.add(Diff(Operation.delete, text)); } - break; default: // Anything else is an error. throw ArgumentError('Invalid diff operation in diff_fromDelta: ${token[0]}'); diff --git a/lib/utils/xmp_utils.dart b/lib/utils/xmp_utils.dart index c46274fa7..0953bac31 100644 --- a/lib/utils/xmp_utils.dart +++ b/lib/utils/xmp_utils.dart @@ -26,7 +26,7 @@ class XMP { static const nsXmp = XmpNamespaces.xmp; // for `rdf:Description` node only - static bool _hasMeaningfulChildren(XmlNode node) => node.children.any((v) => v.nodeType != XmlNodeType.TEXT || v.text.trim().isNotEmpty); + static bool _hasMeaningfulChildren(XmlNode node) => node.children.any((v) => v.nodeType != XmlNodeType.TEXT || v.innerText.trim().isNotEmpty); // for `rdf:Description` node only static bool _hasMeaningfulAttributes(XmlNode description) { diff --git a/lib/widget_common.dart b/lib/widget_common.dart index fe14fee12..4ea33f334 100644 --- a/lib/widget_common.dart +++ b/lib/widget_common.dart @@ -79,10 +79,8 @@ Future _getWidgetEntry(int widgetId, bool reuseEntry) async { switch (settings.getWidgetDisplayedItem(widgetId)) { case WidgetDisplayedItem.random: entries.shuffle(); - break; case WidgetDisplayedItem.mostRecent: entries.sort(AvesEntrySort.compareByDate); - break; } final entry = entries.firstOrNull; if (entry != null) { diff --git a/lib/widgets/about/app_ref.dart b/lib/widgets/about/app_ref.dart index 83c6000d0..af20e6d55 100644 --- a/lib/widgets/about/app_ref.dart +++ b/lib/widgets/about/app_ref.dart @@ -94,11 +94,12 @@ class _AppReferenceState extends State { return FutureBuilder( future: _packageInfoLoader, builder: (context, snapshot) { + final textScaleFactor = MediaQuery.textScaleFactorOf(context); return Row( mainAxisSize: MainAxisSize.min, children: [ AvesLogo( - size: _appTitleStyle.fontSize! * MediaQuery.textScaleFactorOf(context) * 1.3, + size: _appTitleStyle.fontSize! * textScaleFactor * 1.3, ), const SizedBox(width: 8), Text( diff --git a/lib/widgets/about/bug_report.dart b/lib/widgets/about/bug_report.dart index 954146ea2..6cb94eb44 100644 --- a/lib/widgets/about/bug_report.dart +++ b/lib/widgets/about/bug_report.dart @@ -157,7 +157,7 @@ class _BugReportState extends State with FeedbackMixin { 'Device: ${androidInfo.manufacturer} ${androidInfo.model}', 'Geocoder: ${device.hasGeocoder ? 'ready' : 'not available'}', 'Mobile services: ${mobileServices.isServiceAvailable ? 'ready' : 'not available'}', - 'System locales: ${WidgetsBinding.instance.window.locales.join(', ')}', + 'System locales: ${WidgetsBinding.instance.platformDispatcher.locales.join(', ')}', 'Storage volumes: ${storageVolumes.map((v) => v.path).join(', ')}', 'Storage grants: ${storageGrants.join(', ')}', 'Error reporting: ${settings.isErrorReportingAllowed}', diff --git a/lib/widgets/about/tv_license_page.dart b/lib/widgets/about/tv_license_page.dart index fa4e2b7e2..ef229b3e9 100644 --- a/lib/widgets/about/tv_license_page.dart +++ b/lib/widgets/about/tv_license_page.dart @@ -1,7 +1,9 @@ +import 'dart:developer' show Flow, Timeline; + import 'package:aves/widgets/common/basic/scaffold.dart'; import 'package:aves/widgets/common/behaviour/intents.dart'; import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; +import 'package:flutter/material.dart' hide Flow; import 'package:flutter/scheduler.dart'; import 'package:flutter/services.dart'; @@ -209,10 +211,21 @@ class _PackageLicensePageState extends State<_PackageLicensePage> { bool _loaded = false; Future _initLicenses() async { + int debugFlowId = -1; + assert(() { + final Flow flow = Flow.begin(); + Timeline.timeSync('_initLicenses()', () {}, flow: flow); + debugFlowId = flow.id; + return true; + }()); for (final LicenseEntry license in widget.licenseEntries) { if (!mounted) { return; } + assert(() { + Timeline.timeSync('_initLicenses()', () {}, flow: Flow.step(debugFlowId)); + return true; + }()); final List paragraphs = await SchedulerBinding.instance.scheduleTask>( license.paragraphs.toList, Priority.animation, @@ -237,6 +250,7 @@ class _PackageLicensePageState extends State<_PackageLicensePage> { ), )); } else { + assert(paragraph.indent >= 0); _licenses.add(Padding( padding: EdgeInsetsDirectional.only(top: 8.0, start: 16.0 * paragraph.indent), child: Text(paragraph.text), @@ -248,16 +262,21 @@ class _PackageLicensePageState extends State<_PackageLicensePage> { setState(() { _loaded = true; }); + assert(() { + Timeline.timeSync('Build scheduled', () {}, flow: Flow.end(debugFlowId)); + return true; + }()); } @override Widget build(BuildContext context) { + assert(debugCheckHasMaterialLocalizations(context)); final MaterialLocalizations localizations = MaterialLocalizations.of(context); final ThemeData theme = Theme.of(context); final String title = widget.packageName; final String subtitle = localizations.licensesPackageDetailText(widget.licenseEntries.length); - const double pad = 24; - const EdgeInsets padding = EdgeInsets.only(left: pad, right: pad, bottom: pad); + final double pad = _getGutterSize(context); + final EdgeInsets padding = EdgeInsets.only(left: pad, right: pad, bottom: pad); final List listWidgets = [ ..._licenses, if (!_loaded) @@ -274,9 +293,11 @@ class _PackageLicensePageState extends State<_PackageLicensePage> { page = Scaffold( appBar: AppBar( title: _PackageLicensePageTitle( - title, - subtitle, - theme.primaryTextTheme, + title: title, + subtitle: subtitle, + theme: theme.useMaterial3 ? theme.textTheme : theme.primaryTextTheme, + titleTextStyle: theme.appBarTheme.titleTextStyle, + foregroundColor: theme.appBarTheme.foregroundColor, ), ), body: Center( @@ -292,7 +313,11 @@ class _PackageLicensePageState extends State<_PackageLicensePage> { // A Scrollbar is built-in below. behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false), child: Scrollbar( - child: ListView(padding: padding, children: listWidgets), + child: ListView( + primary: true, + padding: padding, + children: listWidgets, + ), ), ), ), @@ -308,7 +333,12 @@ class _PackageLicensePageState extends State<_PackageLicensePage> { automaticallyImplyLeading: false, pinned: true, backgroundColor: theme.cardColor, - title: _PackageLicensePageTitle(title, subtitle, theme.textTheme), + title: _PackageLicensePageTitle( + title: title, + subtitle: subtitle, + theme: theme.textTheme, + titleTextStyle: theme.textTheme.titleLarge, + ), ), SliverPadding( padding: padding, @@ -334,27 +364,36 @@ class _PackageLicensePageState extends State<_PackageLicensePage> { } class _PackageLicensePageTitle extends StatelessWidget { - const _PackageLicensePageTitle( - this.title, - this.subtitle, - this.theme, - ); + const _PackageLicensePageTitle({ + required this.title, + required this.subtitle, + required this.theme, + this.titleTextStyle, + this.foregroundColor, + }); final String title; final String subtitle; final TextTheme theme; + final TextStyle? titleTextStyle; + final Color? foregroundColor; @override Widget build(BuildContext context) { - final Color? color = Theme.of(context).appBarTheme.foregroundColor; - + final TextStyle? effectiveTitleTextStyle = titleTextStyle ?? theme.titleLarge; return Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(title, style: theme.titleLarge?.copyWith(color: color)), - Text(subtitle, style: theme.titleSmall?.copyWith(color: color)), + Text(title, style: effectiveTitleTextStyle?.copyWith(color: foregroundColor)), + Text(subtitle, style: theme.titleSmall?.copyWith(color: foregroundColor)), ], ); } } + +const int _materialGutterThreshold = 720; +const double _wideGutterSize = 24.0; +const double _narrowGutterSize = 12.0; + +double _getGutterSize(BuildContext context) => MediaQuery.sizeOf(context).width >= _materialGutterThreshold ? _wideGutterSize : _narrowGutterSize; diff --git a/lib/widgets/aves_app.dart b/lib/widgets/aves_app.dart index 01ef95154..d34dfd3a4 100644 --- a/lib/widgets/aves_app.dart +++ b/lib/widgets/aves_app.dart @@ -1,6 +1,5 @@ import 'dart:async'; import 'dart:math'; -import 'dart:ui'; import 'package:aves/app_flavor.dart'; import 'package:aves/app_mode.dart'; @@ -180,8 +179,6 @@ class _AvesAppState extends State with WidgetsBindingObserver { super.initState(); EquatableConfig.stringify = true; _appSetup = _setup(); - // remember screen size to use it later, when `context` and `window` are no longer reliable - _screenSize = _getScreenSize(); _shouldUseBoldFontLoader = AccessibilityService.shouldUseBoldFont(); _dynamicColorPaletteLoader = DynamicColorPlugin.getCorePalette(); _subscriptions.add(_mediaStoreChangeChannel.receiveBroadcastStream().listen((event) => _onMediaStoreChanged(event as String?))); @@ -206,6 +203,9 @@ class _AvesAppState extends State with WidgetsBindingObserver { @override Widget build(BuildContext context) { + // remember screen size to use it later, when `context` and `window` are no longer reliable + _screenSize ??= _getScreenSize(context); + // place the settings provider above `MaterialApp` // so it can be used during navigation transitions return MultiProvider( @@ -266,38 +266,31 @@ class _AvesAppState extends State with WidgetsBindingObserver { // KEYCODE_ENTER, KEYCODE_BUTTON_A, KEYCODE_NUMPAD_ENTER LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(), }, - child: MediaQuery.fromWindow( - child: Builder( - builder: (context) { - return MediaQuery( - data: MediaQuery.of(context).copyWith( - // disable accessible navigation, as it impacts snack bar action timer - // for all users of apps registered as accessibility services, - // even though they are not for accessibility purposes (like TalkBack is) - accessibleNavigation: false, - ), - child: MaterialApp( - navigatorKey: _navigatorKey, - home: home, - navigatorObservers: _navigatorObservers, - builder: (context, child) => _decorateAppChild( - context: context, - initialized: initialized, - child: child, - ), - onGenerateTitle: (context) => context.l10n.appName, - theme: lightTheme, - darkTheme: darkTheme, - themeMode: themeBrightness.appThemeMode, - locale: settingsLocale, - localizationsDelegates: AppLocalizations.localizationsDelegates, - supportedLocales: AvesApp.supportedLocales, - // TODO TLAD remove custom scroll behavior when this is fixed: https://github.com/flutter/flutter/issues/82906 - scrollBehavior: StretchMaterialScrollBehavior(), - useInheritedMediaQuery: true, - ), - ); - }, + child: MediaQuery( + data: MediaQuery.of(context).copyWith( + // disable accessible navigation, as it impacts snack bar action timer + // for all users of apps registered as accessibility services, + // even though they are not for accessibility purposes (like TalkBack is) + accessibleNavigation: false, + ), + child: MaterialApp( + navigatorKey: _navigatorKey, + home: home, + navigatorObservers: _navigatorObservers, + builder: (context, child) => _decorateAppChild( + context: context, + initialized: initialized, + child: child, + ), + onGenerateTitle: (context) => context.l10n.appName, + theme: lightTheme, + darkTheme: darkTheme, + themeMode: themeBrightness.appThemeMode, + locale: settingsLocale, + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AvesApp.supportedLocales, + // TODO TLAD remove custom scroll behavior when this is fixed: https://github.com/flutter/flutter/issues/82906 + scrollBehavior: StretchMaterialScrollBehavior(), ), ), ); @@ -390,7 +383,6 @@ class _AvesAppState extends State with WidgetsBindingObserver { case AppMode.pickSingleMediaExternal: case AppMode.pickMultipleMediaExternal: _saveTopEntries(); - break; case AppMode.pickCollectionFiltersExternal: case AppMode.pickMediaInternal: case AppMode.pickFilterInternal: @@ -400,10 +392,8 @@ class _AvesAppState extends State with WidgetsBindingObserver { case AppMode.view: break; } - break; case AppLifecycleState.resumed: RecentlyAddedFilter.updateNow(); - break; case AppLifecycleState.paused: case AppLifecycleState.detached: break; @@ -421,9 +411,10 @@ class _AvesAppState extends State with WidgetsBindingObserver { Widget _getFirstPage({Map? intentData}) => settings.hasAcceptedTerms ? HomePage(intentData: intentData) : const WelcomePage(); - Size? _getScreenSize() { - final physicalSize = window.physicalSize; - final ratio = window.devicePixelRatio; + Size? _getScreenSize(BuildContext context) { + final view = View.of(context); + final physicalSize = view.physicalSize; + final ratio = view.devicePixelRatio; return physicalSize > Size.zero && ratio > 0 ? physicalSize / ratio : null; } @@ -431,7 +422,7 @@ class _AvesAppState extends State with WidgetsBindingObserver { void _saveTopEntries() { if (!settings.initialized) return; - final screenSize = _screenSize ?? _getScreenSize(); + final screenSize = _screenSize; if (screenSize == null) return; var tileExtent = settings.getTileExtent(CollectionPage.routeName); @@ -525,10 +516,8 @@ class _AvesAppState extends State with WidgetsBindingObserver { case MaxBrightness.never: case MaxBrightness.viewerOnly: ScreenBrightness().resetScreenBrightness(); - break; case MaxBrightness.always: ScreenBrightness().setScreenBrightness(1); - break; } } @@ -586,7 +575,7 @@ class _AvesAppState extends State with WidgetsBindingObserver { : 'debug', 'has_mobile_services': mobileServices.isServiceAvailable, 'is_television': device.isTelevision, - 'locales': WidgetsBinding.instance.window.locales.join(', '), + 'locales': WidgetsBinding.instance.platformDispatcher.locales.join(', '), 'time_zone': '${now.timeZoneName} (${now.timeZoneOffset})', }); await reportService.log('Launch'); diff --git a/lib/widgets/collection/app_bar.dart b/lib/widgets/collection/app_bar.dart index f3db7ad22..63a181814 100644 --- a/lib/widgets/collection/app_bar.dart +++ b/lib/widgets/collection/app_bar.dart @@ -224,7 +224,7 @@ class _CollectionAppBarState extends State with SingleTickerPr } double get appBarContentHeight { - final textScaleFactor = context.read().textScaleFactor; + final textScaleFactor = MediaQuery.textScaleFactorOf(context); double height = kToolbarHeight * textScaleFactor; if (settings.useTvLayout) { height += CaptionedButton.getTelevisionButtonHeight(context); @@ -511,16 +511,13 @@ class _CollectionAppBarState extends State with SingleTickerPr queryEnabled: context.read().enabled, isMenuItem: true, ); - break; case EntrySetAction.toggleFavourite: child = FavouriteToggler( entries: _getExpandedSelectedItems(selection), isMenuItem: true, ); - break; default: child = MenuRow(text: action.getText(context), icon: action.getIcon()); - break; } return PopupMenuItem( key: _getActionKey(action), @@ -598,7 +595,7 @@ class _CollectionAppBarState extends State with SingleTickerPr void _onQueryFocusRequest() => _queryBarFocusNode.requestFocus(); void _updateStatusBarHeight() { - _statusBarHeight = context.read().padding.top; + _statusBarHeight = MediaQuery.paddingOf(context).top; _updateAppBarHeight(); } @@ -611,16 +608,12 @@ class _CollectionAppBarState extends State with SingleTickerPr // general case EntrySetAction.configureView: await _configureView(); - break; case EntrySetAction.select: context.read>().select(); - break; case EntrySetAction.selectAll: context.read>().addToSelection(collection.sortedEntries); - break; case EntrySetAction.selectNone: context.read>().clearSelection(); - break; // browsing case EntrySetAction.searchCollection: case EntrySetAction.toggleTitleSearch: @@ -650,7 +643,6 @@ class _CollectionAppBarState extends State with SingleTickerPr case EntrySetAction.editTags: case EntrySetAction.removeMetadata: _actionDelegate.onActionSelected(context, action); - break; } } diff --git a/lib/widgets/collection/collection_grid.dart b/lib/widgets/collection/collection_grid.dart index 23d4c148a..059bd0a63 100644 --- a/lib/widgets/collection/collection_grid.dart +++ b/lib/widgets/collection/collection_grid.dart @@ -46,7 +46,6 @@ import 'package:aves/widgets/navigation/nav_bar/nav_bar.dart'; import 'package:aves/widgets/viewer/entry_viewer_page.dart'; import 'package:aves_model/aves_model.dart'; import 'package:collection/collection.dart'; -import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:intl/intl.dart'; @@ -374,6 +373,8 @@ class _CollectionScaler extends StatelessWidget { final tileSpacing = metrics.item1; final horizontalPadding = metrics.item2; final brightness = Theme.of(context).brightness; + final borderColor = DecoratedThumbnail.borderColor; + final borderWidth = DecoratedThumbnail.borderWidth(context); return GridScaleGestureDetector( scrollableKey: scrollableKey, tileLayout: tileLayout, @@ -385,9 +386,9 @@ class _CollectionScaler extends StatelessWidget { tileSize: tileSize, spacing: tileSpacing, horizontalPadding: horizontalPadding, - borderWidth: DecoratedThumbnail.borderWidth, + borderWidth: borderWidth, borderRadius: Radius.zero, - color: DecoratedThumbnail.borderColor, + color: borderColor, textDirection: Directionality.of(context), ), child: child, @@ -404,8 +405,8 @@ class _CollectionScaler extends StatelessWidget { decoration: BoxDecoration( color: ThumbnailImage.computeLoadingBackgroundColor(index * 10, brightness).withOpacity(.9), border: Border.all( - color: DecoratedThumbnail.borderColor, - width: DecoratedThumbnail.borderWidth, + color: borderColor, + width: borderWidth, ), ), ), @@ -489,7 +490,6 @@ class _CollectionScrollViewState extends State<_CollectionScrollView> with Widge } }); } - break; } } @@ -573,7 +573,7 @@ class _CollectionScrollViewState extends State<_CollectionScrollView> with Widge physics: collection.isEmpty ? const NeverScrollableScrollPhysics() : SloppyScrollPhysics( - gestureSettings: context.select((mq) => mq.gestureSettings), + gestureSettings: MediaQuery.gestureSettingsOf(context), parent: const AlwaysScrollableScrollPhysics(), ), cacheExtent: context.select((controller) => controller.effectiveExtentMax), @@ -677,7 +677,6 @@ class _CollectionScrollViewState extends State<_CollectionScrollView> with Widge switch (collection.sectionFactor) { case EntryGroupFactor.album: addAlbums(collection, sectionLayouts, crumbs); - break; case EntryGroupFactor.month: case EntryGroupFactor.day: final firstKey = sectionLayouts.first.sectionKey; @@ -701,14 +700,11 @@ class _CollectionScrollViewState extends State<_CollectionScrollView> with Widge }); } } - break; case EntryGroupFactor.none: break; } - break; case EntrySortFactor.name: addAlbums(collection, sectionLayouts, crumbs); - break; case EntrySortFactor.rating: case EntrySortFactor.size: break; diff --git a/lib/widgets/collection/entry_set_action_delegate.dart b/lib/widgets/collection/entry_set_action_delegate.dart index 908ad0861..2eb7a9bdc 100644 --- a/lib/widgets/collection/entry_set_action_delegate.dart +++ b/lib/widgets/collection/entry_set_action_delegate.dart @@ -173,79 +173,55 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware // browsing case EntrySetAction.searchCollection: _goToSearch(context); - break; case EntrySetAction.toggleTitleSearch: context.read().toggle(); - break; case EntrySetAction.addShortcut: _addShortcut(context); - break; // browsing or selecting case EntrySetAction.map: _goToMap(context); - break; case EntrySetAction.slideshow: _goToSlideshow(context); - break; case EntrySetAction.stats: _goToStats(context); - break; case EntrySetAction.rescan: _rescan(context); - break; // selecting case EntrySetAction.share: _share(context); - break; case EntrySetAction.delete: case EntrySetAction.emptyBin: _delete(context); - break; case EntrySetAction.restore: _move(context, moveType: MoveType.fromBin); - break; case EntrySetAction.copy: _move(context, moveType: MoveType.copy); - break; case EntrySetAction.move: _move(context, moveType: MoveType.move); - break; case EntrySetAction.rename: _rename(context); - break; case EntrySetAction.convert: _convert(context); - break; case EntrySetAction.toggleFavourite: _toggleFavourite(context); - break; case EntrySetAction.rotateCCW: _rotate(context, clockwise: false); - break; case EntrySetAction.rotateCW: _rotate(context, clockwise: true); - break; case EntrySetAction.flip: _flip(context); - break; case EntrySetAction.editDate: editDate(context); - break; case EntrySetAction.editLocation: _editLocation(context); - break; case EntrySetAction.editTitleDescription: _editTitleDescription(context); - break; case EntrySetAction.editRating: _editRating(context); - break; case EntrySetAction.editTags: _editTags(context); - break; case EntrySetAction.removeMetadata: _removeMetadata(context); - break; } } diff --git a/lib/widgets/collection/grid/headers/any.dart b/lib/widgets/collection/grid/headers/any.dart index c99c0c49d..556380fcc 100644 --- a/lib/widgets/collection/grid/headers/any.dart +++ b/lib/widgets/collection/grid/headers/any.dart @@ -57,7 +57,6 @@ class CollectionSectionHeader extends StatelessWidget { case EntryGroupFactor.none: break; } - break; case EntrySortFactor.name: return _buildAlbumHeader(context); case EntrySortFactor.rating: diff --git a/lib/widgets/collection/grid/list_details.dart b/lib/widgets/collection/grid/list_details.dart index 4d680b81d..1175dc0bd 100644 --- a/lib/widgets/collection/grid/list_details.dart +++ b/lib/widgets/collection/grid/list_details.dart @@ -78,7 +78,7 @@ class EntryListDetails extends StatelessWidget { Widget _buildDateRow(BuildContext context, TextStyle style) { final locale = context.l10n.localeName; - final use24hour = context.select((v) => v.alwaysUse24HourFormat); + final use24hour = MediaQuery.alwaysUse24HourFormatOf(context); final date = entry.bestDate; final dateText = date != null ? formatDateTime(date, locale, use24hour) : AText.valueNotAvailable; diff --git a/lib/widgets/collection/grid/tile.dart b/lib/widgets/collection/grid/tile.dart index da346a880..a4e132d0f 100644 --- a/lib/widgets/collection/grid/tile.dart +++ b/lib/widgets/collection/grid/tile.dart @@ -41,17 +41,13 @@ class InteractiveTile extends StatelessWidget { } else { OpenViewerNotification(entry).dispatch(context); } - break; case AppMode.pickSingleMediaExternal: IntentService.submitPickedItems([entry.uri]); - break; case AppMode.pickMultipleMediaExternal: final selection = context.read>(); selection.toggleSelection(entry); - break; case AppMode.pickMediaInternal: Navigator.maybeOf(context)?.pop(entry); - break; case AppMode.pickCollectionFiltersExternal: case AppMode.pickFilterInternal: case AppMode.screenSaver: diff --git a/lib/widgets/collection/query_bar.dart b/lib/widgets/collection/query_bar.dart index 005c7ec02..70a7a43e1 100644 --- a/lib/widgets/collection/query_bar.dart +++ b/lib/widgets/collection/query_bar.dart @@ -52,7 +52,7 @@ class _EntryQueryBarState extends State { @override Widget build(BuildContext context) { - final textScaleFactor = context.select((mq) => mq.textScaleFactor); + final textScaleFactor = MediaQuery.textScaleFactorOf(context); return Container( height: EntryQueryBar.getPreferredHeight(textScaleFactor), alignment: Alignment.topCenter, diff --git a/lib/widgets/common/action_controls/quick_choosers/common/menu.dart b/lib/widgets/common/action_controls/quick_choosers/common/menu.dart index 5beae8e82..aff175503 100644 --- a/lib/widgets/common/action_controls/quick_choosers/common/menu.dart +++ b/lib/widgets/common/action_controls/quick_choosers/common/menu.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:ui'; import 'package:aves/theme/durations.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/common/quick_chooser.dart'; @@ -51,10 +50,15 @@ class _MenuQuickChooserState extends State> { @override void initState() { super.initState(); - _selectedRowRect.value = Rect.fromLTWH(0, window.physicalSize.height * (reversed ? 1 : -1), 0, 0); _registerWidget(widget); } + @override + void didChangeDependencies() { + super.didChangeDependencies(); + _selectedRowRect.value = Rect.fromLTWH(0, MediaQuery.sizeOf(context).height * (reversed ? 1 : -1), 0, 0); + } + @override void didUpdateWidget(covariant MenuQuickChooser oldWidget) { super.didUpdateWidget(oldWidget); diff --git a/lib/widgets/common/action_controls/quick_choosers/common/route_layout.dart b/lib/widgets/common/action_controls/quick_choosers/common/route_layout.dart index 07a2b4624..835f69107 100644 --- a/lib/widgets/common/action_controls/quick_choosers/common/route_layout.dart +++ b/lib/widgets/common/action_controls/quick_choosers/common/route_layout.dart @@ -33,10 +33,8 @@ class QuickChooserRouteLayout extends SingleChildLayoutDelegate { switch (menuPosition) { case PopupMenuPosition.over: y = triggerRect.top - childSize.height; - break; case PopupMenuPosition.under: y = size.height - triggerRect.bottom; - break; } double x = (triggerRect.left + (size.width - triggerRect.right) - childSize.width) / 2; final wantedPosition = Offset(x, y); diff --git a/lib/widgets/common/action_mixins/entry_storage.dart b/lib/widgets/common/action_mixins/entry_storage.dart index 24ecffa80..c41508ae1 100644 --- a/lib/widgets/common/action_mixins/entry_storage.dart +++ b/lib/widgets/common/action_mixins/entry_storage.dart @@ -309,17 +309,14 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin { ..remove(destinationAlbum) ..insert(0, destinationAlbum); entriesByDestination[destinationAlbum] = entries; - break; case MoveType.toBin: entriesByDestination[AndroidFileUtils.trashDirPath] = entries; - break; case MoveType.fromBin: groupBy(entries, (e) => e.directory).forEach((originAlbum, dirEntries) { if (originAlbum != null) { entriesByDestination[originAlbum] = dirEntries.toSet(); } }); - break; } await doQuickMove( diff --git a/lib/widgets/common/action_mixins/overlay_snack_bar.dart b/lib/widgets/common/action_mixins/overlay_snack_bar.dart index 778654cca..1c212f712 100644 --- a/lib/widgets/common/action_mixins/overlay_snack_bar.dart +++ b/lib/widgets/common/action_mixins/overlay_snack_bar.dart @@ -8,66 +8,82 @@ import 'package:flutter/material.dart'; // This overlay entry is not below a `Scaffold` (which is expected by `SnackBar` // and `SnackBarAction`), and is not dismissed the same way. // This adaptation assumes the `SnackBarBehavior.floating` behavior and no animation. -class OverlaySnackBar extends StatelessWidget { +class OverlaySnackBar extends StatefulWidget { final Widget content; final Widget? action; final DismissDirection dismissDirection; final VoidCallback onDismiss; + final Clip clipBehavior; const OverlaySnackBar({ super.key, required this.content, - required this.action, - required this.dismissDirection, + this.action, + this.dismissDirection = DismissDirection.down, + this.clipBehavior = Clip.hardEdge, required this.onDismiss, }); + @override + State createState() => _OverlaySnackBarState(); +} + +class _OverlaySnackBarState extends State { @override Widget build(BuildContext context) { - final theme = Theme.of(context); - final colorScheme = theme.colorScheme; - final snackBarTheme = theme.snackBarTheme; - final isThemeDark = theme.brightness == Brightness.dark; - final buttonColor = isThemeDark ? colorScheme.primary : colorScheme.secondary; + assert(debugCheckHasMediaQuery(context)); + final ThemeData theme = Theme.of(context); + final ColorScheme colorScheme = theme.colorScheme; + final SnackBarThemeData snackBarTheme = theme.snackBarTheme; + final bool isThemeDark = theme.brightness == Brightness.dark; + final Color buttonColor = isThemeDark ? colorScheme.primary : colorScheme.secondary; + final SnackBarThemeData defaults = theme.useMaterial3 ? _SnackbarDefaultsM3(context) : _SnackbarDefaultsM2(context); - final brightness = isThemeDark ? Brightness.light : Brightness.dark; - final themeBackgroundColor = isThemeDark ? colorScheme.onSurface : Color.alphaBlend(colorScheme.onSurface.withOpacity(0.80), colorScheme.surface); - final inverseTheme = theme.copyWith( - colorScheme: ColorScheme( - primary: colorScheme.onPrimary, - secondary: buttonColor, - surface: colorScheme.onSurface, - background: themeBackgroundColor, - error: colorScheme.onError, - onPrimary: colorScheme.primary, - onSecondary: colorScheme.secondary, - onSurface: colorScheme.surface, - onBackground: colorScheme.background, - onError: colorScheme.error, - brightness: brightness, - ), - ); + // SnackBar uses a theme that is the opposite brightness from + // the surrounding theme. + final Brightness brightness = isThemeDark ? Brightness.light : Brightness.dark; - final contentTextStyle = snackBarTheme.contentTextStyle ?? ThemeData(brightness: brightness).textTheme.titleMedium; + // Invert the theme values for Material 2. Material 3 values are tokenized to pre-inverted values. + final ThemeData effectiveTheme = theme.useMaterial3 + ? theme + : theme.copyWith( + colorScheme: ColorScheme( + primary: colorScheme.onPrimary, + secondary: buttonColor, + surface: colorScheme.onSurface, + background: defaults.backgroundColor!, + error: colorScheme.onError, + onPrimary: colorScheme.primary, + onSecondary: colorScheme.secondary, + onSurface: colorScheme.surface, + onBackground: colorScheme.background, + onError: colorScheme.error, + brightness: brightness, + ), + ); + + final TextStyle? contentTextStyle = snackBarTheme.contentTextStyle ?? defaults.contentTextStyle; final horizontalPadding = FeedbackMixin.snackBarHorizontalPadding(snackBarTheme); - final padding = EdgeInsetsDirectional.only(start: horizontalPadding, end: action != null ? 0 : horizontalPadding); + final padding = EdgeInsetsDirectional.only(start: horizontalPadding, end: widget.action != null ? 0 : horizontalPadding); const singleLineVerticalPadding = 14.0; + final EdgeInsets margin = snackBarTheme.insetPadding ?? defaults.insetPadding!; + Widget snackBar = Padding( padding: padding, child: Row( children: [ Expanded( child: Container( - padding: action != null ? null : const EdgeInsets.symmetric(vertical: singleLineVerticalPadding), + padding: widget.action != null ? null : const EdgeInsets.symmetric(vertical: singleLineVerticalPadding), child: DefaultTextStyle( style: contentTextStyle!, - child: content, + child: widget.content, ), ), ), - if (action != null) + if (widget.action != null) TextButtonTheme( data: TextButtonThemeData( style: TextButton.styleFrom( @@ -75,36 +91,28 @@ class OverlaySnackBar extends StatelessWidget { padding: EdgeInsets.symmetric(horizontal: horizontalPadding), ), ), - child: action!, + child: widget.action!, ), ], ), ); - final elevation = snackBarTheme.elevation ?? 6.0; - final backgroundColor = snackBarTheme.backgroundColor ?? inverseTheme.colorScheme.background; - final shape = snackBarTheme.shape ?? const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0))); + final double elevation = snackBarTheme.elevation ?? defaults.elevation!; + final Color backgroundColor = snackBarTheme.backgroundColor ?? defaults.backgroundColor!; + final ShapeBorder? shape = snackBarTheme.shape ?? defaults.shape; snackBar = Material( shape: shape, elevation: elevation, color: backgroundColor, child: Theme( - data: inverseTheme, + data: effectiveTheme, child: snackBar, ), ); - const topMargin = 5.0; - const bottomMargin = 10.0; - const horizontalMargin = 15.0; snackBar = Padding( - padding: const EdgeInsets.fromLTRB( - horizontalMargin, - topMargin, - horizontalMargin, - bottomMargin, - ), + padding: margin, child: snackBar, ); @@ -117,16 +125,138 @@ class OverlaySnackBar extends StatelessWidget { snackBar = Semantics( container: true, liveRegion: true, - onDismiss: onDismiss, + onDismiss: widget.onDismiss, child: Dismissible( key: const Key('dismissible'), - direction: dismissDirection, + direction: widget.dismissDirection, resizeDuration: null, - onDismissed: (direction) => onDismiss(), + onDismissed: (direction) => widget.onDismiss(), child: snackBar, ), ); - return snackBar; + final Widget snackBarTransition = snackBar; + + return Hero( + tag: '', + transitionOnUserGestures: true, + child: ClipRect( + clipBehavior: widget.clipBehavior, + child: snackBarTransition, + ), + ); } } + +// Hand coded defaults based on Material Design 2. +class _SnackbarDefaultsM2 extends SnackBarThemeData { + _SnackbarDefaultsM2(BuildContext context) + : _theme = Theme.of(context), + _colors = Theme.of(context).colorScheme, + super(elevation: 6.0); + + late final ThemeData _theme; + late final ColorScheme _colors; + + @override + Color get backgroundColor => _theme.brightness == Brightness.light ? Color.alphaBlend(_colors.onSurface.withOpacity(0.80), _colors.surface) : _colors.onSurface; + + @override + TextStyle? get contentTextStyle => ThemeData(brightness: _theme.brightness == Brightness.light ? Brightness.dark : Brightness.light).textTheme.titleMedium; + + @override + SnackBarBehavior get behavior => SnackBarBehavior.fixed; + + @override + Color get actionTextColor => _colors.secondary; + + @override + Color get disabledActionTextColor => _colors.onSurface.withOpacity(_theme.brightness == Brightness.light ? 0.38 : 0.3); + + @override + ShapeBorder get shape => const RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(4.0), + ), + ); + + @override + EdgeInsets get insetPadding => const EdgeInsets.fromLTRB(15.0, 5.0, 15.0, 10.0); + + @override + bool get showCloseIcon => false; + + @override + Color get closeIconColor => _colors.onSurface; + + @override + double get actionOverflowThreshold => 0.25; +} + +// BEGIN GENERATED TOKEN PROPERTIES - Snackbar + +// Do not edit by hand. The code between the "BEGIN GENERATED" and +// "END GENERATED" comments are generated from data in the Material +// Design token database by the script: +// dev/tools/gen_defaults/bin/gen_defaults.dart. + +// Token database version: v0_162 + +class _SnackbarDefaultsM3 extends SnackBarThemeData { + _SnackbarDefaultsM3(this.context); + + final BuildContext context; + late final ThemeData _theme = Theme.of(context); + late final ColorScheme _colors = _theme.colorScheme; + + @override + Color get backgroundColor => _colors.inverseSurface; + + @override + Color get actionTextColor => MaterialStateColor.resolveWith((states) { + if (states.contains(MaterialState.disabled)) { + return _colors.inversePrimary; + } + if (states.contains(MaterialState.pressed)) { + return _colors.inversePrimary; + } + if (states.contains(MaterialState.hovered)) { + return _colors.inversePrimary; + } + if (states.contains(MaterialState.focused)) { + return _colors.inversePrimary; + } + return _colors.inversePrimary; + }); + + @override + Color get disabledActionTextColor => _colors.inversePrimary; + + @override + TextStyle get contentTextStyle => Theme.of(context).textTheme.bodyMedium!.copyWith( + color: _colors.onInverseSurface, + ); + + @override + double get elevation => 6.0; + + @override + ShapeBorder get shape => const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0))); + + @override + SnackBarBehavior get behavior => SnackBarBehavior.fixed; + + @override + EdgeInsets get insetPadding => const EdgeInsets.fromLTRB(15.0, 5.0, 15.0, 10.0); + + @override + bool get showCloseIcon => false; + + @override + Color? get closeIconColor => _colors.onInverseSurface; + + @override + double get actionOverflowThreshold => 0.25; +} + +// END GENERATED TOKEN PROPERTIES - Snackbar diff --git a/lib/widgets/common/action_mixins/size_aware.dart b/lib/widgets/common/action_mixins/size_aware.dart index f8998d308..10723ce6f 100644 --- a/lib/widgets/common/action_mixins/size_aware.dart +++ b/lib/widgets/common/action_mixins/size_aware.dart @@ -35,7 +35,6 @@ mixin SizeAwareMixin { case MoveType.copy: case MoveType.export: needed = selection.fold(0, sumSize); - break; case MoveType.move: case MoveType.toBin: case MoveType.fromBin: @@ -46,7 +45,6 @@ mixin SizeAwareMixin { // and we need at least as much space as the largest entry because individual entries are copied then deleted final largestSingle = selection.fold(0, (largest, entry) => max(largest, entry.sizeBytes ?? 0)); needed = max(fromOtherVolumes, largestSingle); - break; } final hasEnoughSpace = needed < free; diff --git a/lib/widgets/common/action_mixins/vault_aware.dart b/lib/widgets/common/action_mixins/vault_aware.dart index 137591878..d900a3382 100644 --- a/lib/widgets/common/action_mixins/vault_aware.dart +++ b/lib/widgets/common/action_mixins/vault_aware.dart @@ -36,7 +36,6 @@ mixin VaultAwareMixin on FeedbackMixin { await reportService.recordError(e, stack); } } - break; case VaultLockType.pattern: final pattern = await showDialog( context: context, @@ -46,7 +45,6 @@ mixin VaultAwareMixin on FeedbackMixin { if (pattern != null) { confirmed = pattern == await securityService.readValue(details.passKey); } - break; case VaultLockType.pin: final pin = await showDialog( context: context, @@ -56,7 +54,6 @@ mixin VaultAwareMixin on FeedbackMixin { if (pin != null) { confirmed = pin == await securityService.readValue(details.passKey); } - break; case VaultLockType.password: final password = await showDialog( context: context, @@ -66,7 +63,6 @@ mixin VaultAwareMixin on FeedbackMixin { if (password != null) { confirmed = password == await securityService.readValue(details.passKey); } - break; } if (confirmed == null || !confirmed) return false; @@ -120,7 +116,6 @@ mixin VaultAwareMixin on FeedbackMixin { await reportService.recordError(e, stack); } } - break; case VaultLockType.pattern: final pattern = await showDialog( context: context, @@ -130,7 +125,6 @@ mixin VaultAwareMixin on FeedbackMixin { if (pattern != null) { return await securityService.writeValue(details.passKey, pattern); } - break; case VaultLockType.pin: final pin = await showDialog( context: context, @@ -140,7 +134,6 @@ mixin VaultAwareMixin on FeedbackMixin { if (pin != null) { return await securityService.writeValue(details.passKey, pin); } - break; case VaultLockType.password: final password = await showDialog( context: context, @@ -150,7 +143,6 @@ mixin VaultAwareMixin on FeedbackMixin { if (password != null) { return await securityService.writeValue(details.passKey, password); } - break; } return false; } diff --git a/lib/widgets/common/app_bar/app_bar_title.dart b/lib/widgets/common/app_bar/app_bar_title.dart index 19283c9d9..a0274db45 100644 --- a/lib/widgets/common/app_bar/app_bar_title.dart +++ b/lib/widgets/common/app_bar/app_bar_title.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; class InteractiveAppBarTitle extends StatelessWidget { final GestureTapCallback? onTap; @@ -13,6 +12,7 @@ class InteractiveAppBarTitle extends StatelessWidget { @override Widget build(BuildContext context) { + final textScaleFactor = MediaQuery.textScaleFactorOf(context); return GestureDetector( onTap: onTap, // use a `Container` with a dummy color to make it expand @@ -20,7 +20,7 @@ class InteractiveAppBarTitle extends StatelessWidget { child: Container( alignment: AlignmentDirectional.centerStart, color: Colors.transparent, - height: kToolbarHeight * context.select((mq) => mq.textScaleFactor), + height: kToolbarHeight * textScaleFactor, child: child, ), ); diff --git a/lib/widgets/common/basic/font_size_icon_theme.dart b/lib/widgets/common/basic/font_size_icon_theme.dart index ea43d4449..7b143fcf8 100644 --- a/lib/widgets/common/basic/font_size_icon_theme.dart +++ b/lib/widgets/common/basic/font_size_icon_theme.dart @@ -11,10 +11,11 @@ class FontSizeIconTheme extends StatelessWidget { @override Widget build(BuildContext context) { + final textScaleFactor = MediaQuery.textScaleFactorOf(context); final iconTheme = IconTheme.of(context); return IconTheme( data: iconTheme.copyWith( - size: iconTheme.size! * MediaQuery.textScaleFactorOf(context), + size: iconTheme.size! * textScaleFactor, ), child: child, ); diff --git a/lib/widgets/common/basic/insets.dart b/lib/widgets/common/basic/insets.dart index fa1a73d46..106756878 100644 --- a/lib/widgets/common/basic/insets.dart +++ b/lib/widgets/common/basic/insets.dart @@ -22,7 +22,7 @@ class BottomGestureAreaProtector extends StatelessWidget { left: 0, right: 0, bottom: 0, - height: context.select((mq) => mq.systemGestureInsets.bottom), + height: MediaQuery.systemGestureInsetsOf(context).bottom, child: GestureDetector( // absorb vertical gestures only onVerticalDragDown: (details) {}, @@ -42,7 +42,7 @@ class TopGestureAreaProtector extends StatelessWidget { left: 0, top: 0, right: 0, - height: context.select((mq) => mq.systemGestureInsets.top), + height: MediaQuery.systemGestureInsetsOf(context).top, child: GestureDetector( // absorb vertical gestures only onVerticalDragDown: (details) {}, @@ -64,7 +64,7 @@ class SideGestureAreaProtector extends StatelessWidget { textDirection: TextDirection.ltr, children: [ SizedBox( - width: context.select((mq) => mq.systemGestureInsets.left), + width: MediaQuery.systemGestureInsetsOf(context).left, child: GestureDetector( // absorb horizontal gestures only onHorizontalDragDown: (details) {}, @@ -73,7 +73,7 @@ class SideGestureAreaProtector extends StatelessWidget { ), const Spacer(), SizedBox( - width: context.select((mq) => mq.systemGestureInsets.right), + width: MediaQuery.systemGestureInsetsOf(context).right, child: GestureDetector( // absorb horizontal gestures only onHorizontalDragDown: (details) {}, diff --git a/lib/widgets/common/basic/list_tiles/reselectable_radio.dart b/lib/widgets/common/basic/list_tiles/reselectable_radio.dart index b3d456627..f6a33aacf 100644 --- a/lib/widgets/common/basic/list_tiles/reselectable_radio.dart +++ b/lib/widgets/common/basic/list_tiles/reselectable_radio.dart @@ -54,11 +54,9 @@ class ReselectableRadioListTile extends StatelessWidget { case ListTileControlAffinity.platform: leading = control; trailing = secondary; - break; case ListTileControlAffinity.trailing: leading = secondary; trailing = control; - break; } return MergeSemantics( child: ListTileTheme.merge( diff --git a/lib/widgets/common/basic/markdown_container.dart b/lib/widgets/common/basic/markdown_container.dart index 14cd63158..62c054210 100644 --- a/lib/widgets/common/basic/markdown_container.dart +++ b/lib/widgets/common/basic/markdown_container.dart @@ -54,7 +54,7 @@ class MarkdownContainer extends StatelessWidget { margin: const EdgeInsets.symmetric(horizontal: 8), decoration: BoxDecoration( color: Theme.of(context).canvasColor, - border: Border.all(color: Theme.of(context).dividerColor, width: AvesBorder.curvedBorderWidth), + border: Border.all(color: Theme.of(context).dividerColor, width: AvesBorder.curvedBorderWidth(context)), borderRadius: const BorderRadius.all(Radius.circular(16)), ), constraints: BoxConstraints(maxWidth: useTvLayout ? double.infinity : mobileMaxWidth), diff --git a/lib/widgets/common/basic/scaffold.dart b/lib/widgets/common/basic/scaffold.dart index e70c0fdda..81058a618 100644 --- a/lib/widgets/common/basic/scaffold.dart +++ b/lib/widgets/common/basic/scaffold.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; class AvesScaffold extends StatelessWidget { final PreferredSizeWidget? appBar; @@ -26,7 +25,7 @@ class AvesScaffold extends StatelessWidget { @override Widget build(BuildContext context) { // prevent conflict between drawer drag gesture and Android navigation gestures - final drawerEnableOpenDragGesture = context.select((mq) => mq.systemGestureInsets.horizontal == 0); + final drawerEnableOpenDragGesture = MediaQuery.systemGestureInsetsOf(context).horizontal == 0; return Scaffold( appBar: appBar, diff --git a/lib/widgets/common/basic/text/outlined.dart b/lib/widgets/common/basic/text/outlined.dart index 22630b843..3ccf14529 100644 --- a/lib/widgets/common/basic/text/outlined.dart +++ b/lib/widgets/common/basic/text/outlined.dart @@ -31,44 +31,51 @@ class OutlinedText extends StatelessWidget { @override Widget build(BuildContext context) { // TODO TLAD [subtitles] fix background area for mixed alphabetic-ideographic text - // as of Flutter v2.2.2, the area computed for `backgroundColor` has inconsistent height - // in case of mixed alphabetic-ideographic text. The painted boxes depends on the script. + // as of Flutter v3.10.0, the area computed for `backgroundColor` has inconsistent height + // in case of mixed alphabetic-ideographic text. The painted boxes depend on the script. // Possible workarounds would be to use metrics from: // - `TextPainter.getBoxesForSelection` // - `Paragraph.getBoxesForRange` // and paint the background at the bottom of the `Stack` + final hasOutline = outlineWidth > 0; + + Widget? outline; + if (hasOutline) { + outline = Text.rich( + TextSpan( + children: textSpans.map(_toStrokeSpan).toList(), + ), + textAlign: textAlign, + softWrap: softWrap, + overflow: overflow, + maxLines: maxLines, + ); + if (outlineBlurSigma > 0) { + outline = ImageFiltered( + imageFilter: ImageFilter.blur( + sigmaX: outlineBlurSigma, + sigmaY: outlineBlurSigma, + ), + child: outline, + ); + } + } + + final fill = Text.rich( + TextSpan( + children: hasOutline ? textSpans.map(_toFillSpan).toList() : textSpans, + ), + textAlign: textAlign, + softWrap: softWrap, + overflow: overflow, + maxLines: maxLines, + ); + return Stack( children: [ - if (hasOutline) - ImageFiltered( - imageFilter: outlineBlurSigma > 0 - ? ImageFilter.blur( - sigmaX: outlineBlurSigma, - sigmaY: outlineBlurSigma, - ) - : ImageFilter.matrix( - Matrix4.identity().storage, - ), - child: Text.rich( - TextSpan( - children: textSpans.map(_toStrokeSpan).toList(), - ), - textAlign: textAlign, - softWrap: softWrap, - overflow: overflow, - maxLines: maxLines, - ), - ), - Text.rich( - TextSpan( - children: hasOutline ? textSpans.map(_toFillSpan).toList() : textSpans, - ), - textAlign: textAlign, - softWrap: softWrap, - overflow: overflow, - maxLines: maxLines, - ), + if (outline != null) outline, + fill, ], ); } @@ -89,6 +96,7 @@ class OutlinedText extends StatelessWidget { children: span.children, style: (span.style ?? const TextStyle()).copyWith( backgroundColor: Colors.transparent, + shadows: [], ), ); } diff --git a/lib/widgets/common/basic/wheel.dart b/lib/widgets/common/basic/wheel.dart index b6f190380..1682eb5cb 100644 --- a/lib/widgets/common/basic/wheel.dart +++ b/lib/widgets/common/basic/wheel.dart @@ -46,10 +46,11 @@ class _WheelSelectorState extends State> { @override Widget build(BuildContext context) { + final textScaleFactor = MediaQuery.textScaleFactorOf(context); const background = Colors.transparent; final foreground = DefaultTextStyle.of(context).style.color!; final transitionDuration = context.select((v) => v.formTransition); - final itemSize = Size.square(40 * context.select((mq) => mq.textScaleFactor)); + final itemSize = Size.square(40 * textScaleFactor); return FocusableActionDetector( shortcuts: const { @@ -138,10 +139,8 @@ class _WheelSelectorState extends State> { switch (intent.type) { case _ValueAdjustmentType.up: delta = -1; - break; case _ValueAdjustmentType.down: delta = 1; - break; } final targetItem = _controller.selectedItem + delta; final duration = context.read().formTransition; diff --git a/lib/widgets/common/behaviour/eager_scale_gesture_recognizer.dart b/lib/widgets/common/behaviour/eager_scale_gesture_recognizer.dart index 9149e0b63..ce2b4c682 100644 --- a/lib/widgets/common/behaviour/eager_scale_gesture_recognizer.dart +++ b/lib/widgets/common/behaviour/eager_scale_gesture_recognizer.dart @@ -26,20 +26,47 @@ enum _ScaleState { } class _PointerPanZoomData { - _PointerPanZoomData({required this.focalPoint, required this.scale, required this.rotation}); + _PointerPanZoomData.fromStartEvent(this.parent, PointerPanZoomStartEvent event) + : _position = event.position, + _pan = Offset.zero, + _scale = 1, + _rotation = 0; - Offset focalPoint; - double scale; - double rotation; + _PointerPanZoomData.fromUpdateEvent(this.parent, PointerPanZoomUpdateEvent event) + : _position = event.position, + _pan = event.pan, + _scale = event.scale, + _rotation = event.rotation; + + final EagerScaleGestureRecognizer parent; + final Offset _position; + final Offset _pan; + final double _scale; + final double _rotation; + + Offset get focalPoint { + if (parent.trackpadScrollCausesScale) { + return _position; + } + return _position + _pan; + } + + double get scale { + if (parent.trackpadScrollCausesScale) { + return _scale * math.exp((_pan.dx * parent.trackpadScrollToScaleFactor.dx) + (_pan.dy * parent.trackpadScrollToScaleFactor.dy)); + } + return _scale; + } + + double get rotation => _rotation; @override - String toString() => '_PointerPanZoomData(focalPoint: $focalPoint, scale: $scale, angle: $rotation)'; + String toString() => '_PointerPanZoomData(parent: $parent, _position: $_position, _pan: $_pan, _scale: $_scale, _rotation: $_rotation)'; } //////////////////////////////////////////////////////////////////////////////// bool _isFlingGesture(Velocity velocity) { - assert(velocity != null); final double speedSquared = velocity.pixelsPerSecond.distanceSquared; return speedSquared > kMinFlingVelocity * kMinFlingVelocity; } @@ -57,9 +84,7 @@ class _LineBetweenPointers { this.pointerStartId = 0, this.pointerEndLocation = Offset.zero, this.pointerEndId = 1, - }) : assert(pointerStartLocation != null && pointerEndLocation != null), - assert(pointerStartId != null && pointerEndId != null), - assert(pointerStartId != pointerEndId); + }) : assert(pointerStartId != pointerEndId); // The location and the id of the pointer that marks the start of the line. final Offset pointerStartLocation; @@ -74,23 +99,22 @@ class _LineBetweenPointers { /// /// [ScaleGestureRecognizer] tracks the pointers in contact with the screen and /// calculates their focal point, indicated scale, and rotation. When a focal -/// pointer is established, the recognizer calls [onStart]. As the focal point, -/// scale, rotation change, the recognizer calls [onUpdate]. When the pointers -/// are no longer in contact with the screen, the recognizer calls [onEnd]. +/// point is established, the recognizer calls [onStart]. As the focal point, +/// scale, and rotation change, the recognizer calls [onUpdate]. When the +/// pointers are no longer in contact with the screen, the recognizer calls +/// [onEnd]. class EagerScaleGestureRecognizer extends OneSequenceGestureRecognizer { /// Create a gesture recognizer for interactions intended for scaling content. /// /// {@macro flutter.gestures.GestureRecognizer.supportedDevices} EagerScaleGestureRecognizer({ super.debugOwner, - @Deprecated( - 'Migrate to supportedDevices. ' - 'This feature was deprecated after v2.3.0-1.0.pre.', - ) - super.kind, super.supportedDevices, + super.allowedButtonsFilter, this.dragStartBehavior = DragStartBehavior.down, - }) : assert(dragStartBehavior != null); + this.trackpadScrollCausesScale = false, + this.trackpadScrollToScaleFactor = kDefaultTrackpadScrollToScaleFactor, + }); /// Determines what point is used as the starting point in all calculations /// involving this gesture. @@ -137,6 +161,26 @@ class EagerScaleGestureRecognizer extends OneSequenceGestureRecognizer { Matrix4? _lastTransform; + /// {@template flutter.gestures.scale.trackpadScrollCausesScale} + /// Whether scrolling up/down on a trackpad should cause scaling instead of + /// panning. + /// + /// Defaults to false. + /// {@endtemplate} + bool trackpadScrollCausesScale; + + /// {@template flutter.gestures.scale.trackpadScrollToScaleFactor} + /// A factor to control the direction and magnitude of scale when converting + /// trackpad scrolling. + /// + /// Incoming trackpad pan offsets will be divided by this factor to get scale + /// values. Increasing this offset will reduce the amount of scaling caused by + /// a fixed amount of trackpad scrolling. + /// + /// Defaults to [kDefaultTrackpadScrollToScaleFactor]. + /// {@endtemplate} + Offset trackpadScrollToScaleFactor; + late Offset _initialFocalPoint; Offset? _currentFocalPoint; late double _initialSpan; @@ -151,6 +195,7 @@ class EagerScaleGestureRecognizer extends OneSequenceGestureRecognizer { final Map _pointerLocations = {}; final List _pointerQueue = []; // A queue to sort pointers in order of entrance final Map _velocityTrackers = {}; + VelocityTracker? _scaleVelocityTracker; late Offset _delta; final Map _pointerPanZooms = {}; double _initialPanZoomScaleFactor = 1; @@ -271,15 +316,16 @@ class EagerScaleGestureRecognizer extends OneSequenceGestureRecognizer { _lastTransform = event.transform; } else if (event is PointerPanZoomStartEvent) { assert(_pointerPanZooms[event.pointer] == null); - _pointerPanZooms[event.pointer] = _PointerPanZoomData(focalPoint: event.position, scale: 1, rotation: 0); + _pointerPanZooms[event.pointer] = _PointerPanZoomData.fromStartEvent(this, event); didChangeConfiguration = true; shouldStartIfAccepted = true; + _lastTransform = event.transform; } else if (event is PointerPanZoomUpdateEvent) { assert(_pointerPanZooms[event.pointer] != null); - if (!event.synthesized) { + if (!event.synthesized && !trackpadScrollCausesScale) { _velocityTrackers[event.pointer]!.addPosition(event.timeStamp, event.pan); } - _pointerPanZooms[event.pointer] = _PointerPanZoomData(focalPoint: event.position + event.pan, scale: event.scale, rotation: event.rotation); + _pointerPanZooms[event.pointer] = _PointerPanZoomData.fromUpdateEvent(this, event); _lastTransform = event.transform; shouldStartIfAccepted = true; } else if (event is PointerPanZoomEndEvent) { @@ -292,7 +338,7 @@ class EagerScaleGestureRecognizer extends OneSequenceGestureRecognizer { _update(); if (!didChangeConfiguration || _reconfigure(event.pointer)) { - _advanceStateMachine(shouldStartIfAccepted, event.kind); + _advanceStateMachine(shouldStartIfAccepted, event); } stopTrackingIfPointerNoLongerDown(event); } @@ -403,18 +449,20 @@ class EagerScaleGestureRecognizer extends OneSequenceGestureRecognizer { if (pixelsPerSecond.distanceSquared > kMaxFlingVelocity * kMaxFlingVelocity) { velocity = Velocity(pixelsPerSecond: (pixelsPerSecond / pixelsPerSecond.distance) * kMaxFlingVelocity); } - invokeCallback('onEnd', () => onEnd!(ScaleEndDetails(velocity: velocity, pointerCount: _pointerCount))); + invokeCallback('onEnd', () => onEnd!(ScaleEndDetails(velocity: velocity, scaleVelocity: _scaleVelocityTracker?.getVelocity().pixelsPerSecond.dx ?? -1, pointerCount: _pointerCount))); } else { - invokeCallback('onEnd', () => onEnd!(ScaleEndDetails(pointerCount: _pointerCount))); + invokeCallback('onEnd', () => onEnd!(ScaleEndDetails(scaleVelocity: _scaleVelocityTracker?.getVelocity().pixelsPerSecond.dx ?? -1, pointerCount: _pointerCount))); } } _state = _ScaleState.accepted; + _scaleVelocityTracker = VelocityTracker.withKind(PointerDeviceKind.touch); // arbitrary PointerDeviceKind return false; } + _scaleVelocityTracker = VelocityTracker.withKind(PointerDeviceKind.touch); // arbitrary PointerDeviceKind return true; } - void _advanceStateMachine(bool shouldStartIfAccepted, PointerDeviceKind pointerDeviceKind) { + void _advanceStateMachine(bool shouldStartIfAccepted, PointerEvent event) { if (_state == _ScaleState.ready) { _state = _ScaleState.possible; } @@ -428,7 +476,7 @@ class EagerScaleGestureRecognizer extends OneSequenceGestureRecognizer { if (_state == _ScaleState.possible) { final double spanDelta = (_currentSpan - _initialSpan).abs(); final double focalPointDelta = (_currentFocalPoint! - _initialFocalPoint).distance; - if (spanDelta > computeScaleSlop(pointerDeviceKind) || focalPointDelta > computePanSlop(pointerDeviceKind, gestureSettings) || math.max(_scaleFactor / _pointerScaleFactor, _pointerScaleFactor / _scaleFactor) > 1.05) { + if (spanDelta > computeScaleSlop(event.kind) || focalPointDelta > computePanSlop(event.kind, gestureSettings) || math.max(_scaleFactor / _pointerScaleFactor, _pointerScaleFactor / _scaleFactor) > 1.05) { resolve(GestureDisposition.accepted); } } else if (_state.index >= _ScaleState.accepted.index) { @@ -440,19 +488,22 @@ class EagerScaleGestureRecognizer extends OneSequenceGestureRecognizer { _dispatchOnStartCallbackIfNeeded(); } - if (_state == _ScaleState.started && onUpdate != null) { - invokeCallback('onUpdate', () { - onUpdate!(ScaleUpdateDetails( - scale: _scaleFactor, - horizontalScale: _horizontalScaleFactor, - verticalScale: _verticalScaleFactor, - focalPoint: _currentFocalPoint!, - localFocalPoint: _localFocalPoint, - rotation: _computeRotationFactor(), - pointerCount: _pointerCount, - focalPointDelta: _delta, - )); - }); + if (_state == _ScaleState.started) { + _scaleVelocityTracker?.addPosition(event.timeStamp, Offset(_scaleFactor, 0)); + if (onUpdate != null) { + invokeCallback('onUpdate', () { + onUpdate!(ScaleUpdateDetails( + scale: _scaleFactor, + horizontalScale: _horizontalScaleFactor, + verticalScale: _verticalScaleFactor, + focalPoint: _currentFocalPoint!, + localFocalPoint: _localFocalPoint, + rotation: _computeRotationFactor(), + pointerCount: _pointerCount, + focalPointDelta: _delta, + )); + }); + } } } @@ -504,15 +555,12 @@ class EagerScaleGestureRecognizer extends OneSequenceGestureRecognizer { switch (_state) { case _ScaleState.possible: resolve(GestureDisposition.rejected); - break; case _ScaleState.ready: assert(false); // We should have not seen a pointer yet - break; case _ScaleState.accepted: break; case _ScaleState.started: assert(false); // We should be in the accepted state when user is done - break; } _state = _ScaleState.ready; } diff --git a/lib/widgets/common/behaviour/intents.dart b/lib/widgets/common/behaviour/intents.dart index d58dc2e8f..98dcfd723 100644 --- a/lib/widgets/common/behaviour/intents.dart +++ b/lib/widgets/common/behaviour/intents.dart @@ -14,11 +14,9 @@ class ScrollControllerAction extends CallbackAction { case AxisDirection.up: case AxisDirection.left: factor = -1; - break; case AxisDirection.down: case AxisDirection.right: factor = 1; - break; } scrollController.animateTo( scrollController.offset + factor * 150, diff --git a/lib/widgets/common/behaviour/known_extent_scroll_physics.dart b/lib/widgets/common/behaviour/known_extent_scroll_physics.dart index 0dce20b84..57bb1b809 100644 --- a/lib/widgets/common/behaviour/known_extent_scroll_physics.dart +++ b/lib/widgets/common/behaviour/known_extent_scroll_physics.dart @@ -53,7 +53,7 @@ class KnownExtentScrollPhysics extends ScrollPhysics { // Scenario 3: // If there's no velocity and we're already at where we intend to land, // do nothing. - if (velocity.abs() < tolerance.velocity && (settlingPixels - metrics.pixels).abs() < tolerance.distance) { + if (velocity.abs() < toleranceFor(position).velocity && (settlingPixels - metrics.pixels).abs() < toleranceFor(position).distance) { return null; } @@ -66,7 +66,7 @@ class KnownExtentScrollPhysics extends ScrollPhysics { metrics.pixels, settlingPixels, velocity, - tolerance: tolerance, + tolerance: toleranceFor(position), ); } @@ -77,7 +77,7 @@ class KnownExtentScrollPhysics extends ScrollPhysics { metrics.pixels, settlingPixels, velocity, - tolerance.velocity * velocity.sign, + toleranceFor(position).velocity * velocity.sign, ); } } diff --git a/lib/widgets/common/behaviour/springy_scroll_physics.dart b/lib/widgets/common/behaviour/springy_scroll_physics.dart index 1b39c0540..51b887cde 100644 --- a/lib/widgets/common/behaviour/springy_scroll_physics.dart +++ b/lib/widgets/common/behaviour/springy_scroll_physics.dart @@ -16,5 +16,4 @@ class SpringyScrollPhysics extends ScrollPhysics { parent: buildParent(ancestor), ); } - } diff --git a/lib/widgets/common/fx/borders.dart b/lib/widgets/common/fx/borders.dart index b9dc02a0d..cc474869a 100644 --- a/lib/widgets/common/fx/borders.dart +++ b/lib/widgets/common/fx/borders.dart @@ -1,26 +1,22 @@ -import 'dart:ui'; - import 'package:flutter/material.dart'; class AvesBorder { static Color _borderColor(BuildContext context) => Theme.of(context).brightness == Brightness.dark ? Colors.white30 : Colors.black26; - // directly uses `devicePixelRatio` as it never changes, to avoid visiting ancestors via `MediaQuery` - // 1 device pixel for straight lines is fine - static double get straightBorderWidth => 1 / window.devicePixelRatio; + static double straightBorderWidth(BuildContext context) => 1 / View.of(context).devicePixelRatio; // 1 device pixel for curves is too thin - static double get curvedBorderWidth => window.devicePixelRatio > 2 ? 0.5 : 1.0; + static double curvedBorderWidth(BuildContext context) => View.of(context).devicePixelRatio > 2 ? 0.5 : 1.0; static BorderSide straightSide(BuildContext context, {double? width}) => BorderSide( color: _borderColor(context), - width: width ?? straightBorderWidth, + width: width ?? straightBorderWidth(context), ); static BorderSide curvedSide(BuildContext context, {double? width}) => BorderSide( color: _borderColor(context), - width: width ?? curvedBorderWidth, + width: width ?? curvedBorderWidth(context), ); static Border border(BuildContext context, {double? width}) => Border.fromBorderSide(curvedSide(context, width: width)); diff --git a/lib/widgets/common/fx/transition_image.dart b/lib/widgets/common/fx/transition_image.dart index 4ee1b8390..f9aa1284c 100644 --- a/lib/widgets/common/fx/transition_image.dart +++ b/lib/widgets/common/fx/transition_image.dart @@ -2,6 +2,8 @@ import 'dart:ui' as ui; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:flutter/semantics.dart'; // adapted from Flutter `_ImageState` in `/widgets/image.dart` // and `DecorationImagePainter` in `/painting/decoration_image.dart` @@ -13,6 +15,11 @@ class TransitionImage extends StatefulWidget { final ImageProvider image; final ValueListenable animation; final BoxFit thumbnailFit, viewerFit; + final ImageFrameBuilder? frameBuilder; + final ImageLoadingBuilder? loadingBuilder; + final ImageErrorWidgetBuilder? errorBuilder; + final String? semanticLabel; + final bool excludeFromSemantics; final double? width, height; final bool gaplessPlayback = false; final Color? background; @@ -23,6 +30,11 @@ class TransitionImage extends StatefulWidget { required this.animation, required this.thumbnailFit, required this.viewerFit, + this.frameBuilder, + this.loadingBuilder, + this.errorBuilder, + this.semanticLabel, + this.excludeFromSemantics = false, this.width, this.height, this.background, @@ -32,16 +44,33 @@ class TransitionImage extends StatefulWidget { State createState() => _TransitionImageState(); } -class _TransitionImageState extends State { +class _TransitionImageState extends State with WidgetsBindingObserver { ImageStream? _imageStream; ImageInfo? _imageInfo; + ImageChunkEvent? _loadingProgress; bool _isListeningToStream = false; int? _frameNumber; + bool _wasSynchronouslyLoaded = false; + late DisposableBuildContext> _scrollAwareContext; + Object? _lastException; + StackTrace? _lastStack; + ImageStreamCompleterHandle? _completerHandle; + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + _scrollAwareContext = DisposableBuildContext>(this); + } @override void dispose() { assert(_imageStream != null); + WidgetsBinding.instance.removeObserver(this); _stopListeningToStream(); + _completerHandle?.dispose(); + _scrollAwareContext.dispose(); + _replaceImage(info: null); super.dispose(); } @@ -52,21 +81,23 @@ class _TransitionImageState extends State { if (TickerMode.of(context)) { _listenToStream(); } else { - _stopListeningToStream(); + _stopListeningToStream(keepStreamAlive: true); } super.didChangeDependencies(); } @override - void didUpdateWidget(covariant TransitionImage oldWidget) { + void didUpdateWidget(TransitionImage oldWidget) { super.didUpdateWidget(oldWidget); - if (_isListeningToStream) { + if (_isListeningToStream && (widget.loadingBuilder == null) != (oldWidget.loadingBuilder == null)) { final ImageStreamListener oldListener = _getListener(); _imageStream!.addListener(_getListener(recreateListener: true)); _imageStream!.removeListener(oldListener); } - if (widget.image != oldWidget.image) _resolveImage(); + if (widget.image != oldWidget.image) { + _resolveImage(); + } } @override @@ -76,8 +107,11 @@ class _TransitionImageState extends State { } void _resolveImage() { - final provider = widget.image; - final newStream = provider.resolve(createLocalImageConfiguration( + final ScrollAwareImageProvider provider = ScrollAwareImageProvider( + context: _scrollAwareContext, + imageProvider: widget.image, + ); + final ImageStream newStream = provider.resolve(createLocalImageConfiguration( context, size: widget.width != null && widget.height != null ? Size(widget.width!, widget.height!) : null, )); @@ -88,8 +122,26 @@ class _TransitionImageState extends State { ImageStreamListener _getListener({bool recreateListener = false}) { if (_imageStreamListener == null || recreateListener) { + _lastException = null; + _lastStack = null; _imageStreamListener = ImageStreamListener( _handleImageFrame, + onChunk: widget.loadingBuilder == null ? null : _handleImageChunk, + onError: widget.errorBuilder != null || kDebugMode + ? (error, stackTrace) { + setState(() { + _lastException = error; + _lastStack = stackTrace; + }); + assert(() { + if (widget.errorBuilder == null) { + // ignore: only_throw_errors, since we're just proxying the error. + throw error; // Ensures the error message is printed to the console. + } + return true; + }()); + } + : null, ); } return _imageStreamListener!; @@ -98,15 +150,32 @@ class _TransitionImageState extends State { void _handleImageFrame(ImageInfo imageInfo, bool synchronousCall) { setState(() { _replaceImage(info: imageInfo); + _loadingProgress = null; + _lastException = null; + _lastStack = null; _frameNumber = _frameNumber == null ? 0 : _frameNumber! + 1; + _wasSynchronouslyLoaded = _wasSynchronouslyLoaded | synchronousCall; + }); + } + + void _handleImageChunk(ImageChunkEvent event) { + assert(widget.loadingBuilder != null); + setState(() { + _loadingProgress = event; + _lastException = null; + _lastStack = null; }); } void _replaceImage({required ImageInfo? info}) { - _imageInfo?.dispose(); + final ImageInfo? oldImageInfo = _imageInfo; + SchedulerBinding.instance.addPostFrameCallback((_) => oldImageInfo?.dispose()); _imageInfo = info; } + // Updates _imageStream to newStream, and moves the stream listener + // registration from the old stream to the new stream (if a listener was + // registered). void _updateSourceStream(ImageStream newStream) { if (_imageStream?.key == newStream.key) { return; @@ -123,7 +192,9 @@ class _TransitionImageState extends State { } setState(() { + _loadingProgress = null; _frameNumber = null; + _wasSynchronouslyLoaded = false; }); _imageStream = newStream; @@ -138,22 +209,72 @@ class _TransitionImageState extends State { } _imageStream!.addListener(_getListener()); + _completerHandle?.dispose(); + _completerHandle = null; _isListeningToStream = true; } - void _stopListeningToStream() { + /// Stops listening to the image stream, if this state object has attached a + /// listener. + /// + /// If the listener from this state is the last listener on the stream, the + /// stream will be disposed. To keep the stream alive, set `keepStreamAlive` + /// to true, which create [ImageStreamCompleterHandle] to keep the completer + /// alive and is compatible with the [TickerMode] being off. + void _stopListeningToStream({bool keepStreamAlive = false}) { if (!_isListeningToStream) { return; } + if (keepStreamAlive && _completerHandle == null && _imageStream?.completer != null) { + _completerHandle = _imageStream!.completer!.keepAlive(); + } + _imageStream!.removeListener(_getListener()); _isListeningToStream = false; } + Widget _debugBuildErrorWidget(BuildContext context, Object error) { + return Stack( + alignment: Alignment.center, + children: [ + const Positioned.fill( + child: Placeholder( + color: Color(0xCF8D021F), + ), + ), + Padding( + padding: const EdgeInsets.all(4.0), + child: FittedBox( + child: Text( + '$error', + textAlign: TextAlign.center, + textDirection: TextDirection.ltr, + style: const TextStyle( + shadows: [ + Shadow(blurRadius: 1.0), + ], + ), + ), + ), + ), + ], + ); + } + @override Widget build(BuildContext context) { - return ValueListenableBuilder( + if (_lastException != null) { + if (widget.errorBuilder != null) { + return widget.errorBuilder!(context, _lastException!, _lastStack); + } + if (kDebugMode) { + return _debugBuildErrorWidget(context, _lastException!); + } + } + + Widget result = ValueListenableBuilder( valueListenable: widget.animation, builder: (context, t, child) => CustomPaint( painter: _TransitionImagePainter( @@ -166,6 +287,35 @@ class _TransitionImageState extends State { ), ), ); + + if (!widget.excludeFromSemantics) { + result = Semantics( + container: widget.semanticLabel != null, + image: true, + label: widget.semanticLabel ?? '', + child: result, + ); + } + + if (widget.frameBuilder != null) { + result = widget.frameBuilder!(context, result, _frameNumber, _wasSynchronouslyLoaded); + } + + if (widget.loadingBuilder != null) { + result = widget.loadingBuilder!(context, result, _loadingProgress); + } + + return result; + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder description) { + super.debugFillProperties(description); + description.add(DiagnosticsProperty('stream', _imageStream)); + description.add(DiagnosticsProperty('pixels', _imageInfo)); + description.add(DiagnosticsProperty('loadingProgress', _loadingProgress)); + description.add(DiagnosticsProperty('frameNumber', _frameNumber)); + description.add(DiagnosticsProperty('wasSynchronouslyLoaded', _wasSynchronouslyLoaded)); } } @@ -194,8 +344,11 @@ class _TransitionImagePainter extends CustomPainter { const alignment = Alignment.center; final rect = ui.Rect.fromLTWH(0, 0, size.width, size.height); - final inputSize = Size(image!.width.toDouble(), image!.height.toDouble()); + if (rect.isEmpty) { + return; + } final outputSize = rect.size; + final inputSize = Size(image!.width.toDouble(), image!.height.toDouble()); final thumbnailSizes = applyBoxFit(thumbnailFit, inputSize / scale, size); final viewerSizes = applyBoxFit(viewerFit, inputSize / scale, size); diff --git a/lib/widgets/common/grid/item_tracker.dart b/lib/widgets/common/grid/item_tracker.dart index 9d657887b..74cd97101 100644 --- a/lib/widgets/common/grid/item_tracker.dart +++ b/lib/widgets/common/grid/item_tracker.dart @@ -44,24 +44,18 @@ class _GridItemTrackerState extends State> with WidgetsBin return (scrollableContext.findRenderObject() as RenderBox).size; } - Orientation get _windowOrientation { - final size = WidgetsBinding.instance.window.physicalSize; - return size.width > size.height ? Orientation.landscape : Orientation.portrait; - } - final List _subscriptions = []; // grid section metrics before the app is laid out with the new orientation late SectionedListLayout _lastSectionedListLayout; late Size _lastScrollableSize; - late Orientation _lastOrientation; + Orientation _lastOrientation = Orientation.portrait; @override void initState() { super.initState(); final highlightInfo = context.read(); _subscriptions.add(highlightInfo.eventBus.on>().listen(_trackItem)); - _lastOrientation = _windowOrientation; WidgetsBinding.instance.addObserver(this); _saveLayoutMetrics(); } @@ -78,9 +72,10 @@ class _GridItemTrackerState extends State> with WidgetsBin @override void didChangeMetrics() { // the order of `WidgetsBindingObserver` metrics change notification is unreliable - // w.r.t. the `MediaQuery` update, and consequentially to this widget update: + // w.r.t. the `View` update, and consequentially to this widget update: // `WidgetsBindingObserver` is notified mostly before, sometimes after, the widget update - final orientation = _windowOrientation; + final size = View.of(context).physicalSize; + final orientation = size.width > size.height ? Orientation.landscape : Orientation.portrait; if (_lastOrientation != orientation) { _lastOrientation = orientation; _onLayoutChanged(); @@ -138,7 +133,7 @@ class _GridItemTrackerState extends State> with WidgetsBin Future _saveLayoutMetrics() async { // use a delay to obtain current layout metrics // so that we can handle window orientation change with the previous metrics, - // regardless of the `MediaQuery`/`WidgetsBindingObserver` order uncertainty + // regardless of the `View`/`WidgetsBindingObserver` order uncertainty await Future.delayed(const Duration(milliseconds: 500)); if (mounted) { diff --git a/lib/widgets/common/grid/scaling.dart b/lib/widgets/common/grid/scaling.dart index 767b86cf9..9f4493a61 100644 --- a/lib/widgets/common/grid/scaling.dart +++ b/lib/widgets/common/grid/scaling.dart @@ -57,7 +57,7 @@ class _GridScaleGestureDetectorState extends State((mq) => mq.gestureSettings); + final gestureSettings = MediaQuery.gestureSettingsOf(context); final child = GestureDetector( // Horizontal/vertical drag gestures are interpreted as scaling @@ -116,11 +116,9 @@ class _GridScaleGestureDetectorState extends State extends State extends State extends State extends State { return _initialized ? BoxDecoration( gradient: RadialGradient( - center: FractionalOffset.fromOffsetAndSize(gradientCenter, context.select((mq) => mq.size)), + center: FractionalOffset.fromOffsetAndSize(gradientCenter, MediaQuery.sizeOf(context)), radius: 1, colors: isDark ? const [ diff --git a/lib/widgets/common/grid/sections/mosaic/row.dart b/lib/widgets/common/grid/sections/mosaic/row.dart index 4d0767f96..8d0aae525 100644 --- a/lib/widgets/common/grid/sections/mosaic/row.dart +++ b/lib/widgets/common/grid/sections/mosaic/row.dart @@ -8,7 +8,7 @@ class MosaicGridRow extends MultiChildRenderObjectWidget { final double spacing; final TextDirection textDirection; - MosaicGridRow({ + const MosaicGridRow({ super.key, required this.rowLayout, required this.spacing, diff --git a/lib/widgets/common/identity/aves_app_bar.dart b/lib/widgets/common/identity/aves_app_bar.dart index 3d5b57559..590be02f1 100644 --- a/lib/widgets/common/identity/aves_app_bar.dart +++ b/lib/widgets/common/identity/aves_app_bar.dart @@ -32,73 +32,68 @@ class AvesAppBar extends StatelessWidget { @override Widget build(BuildContext context) { + final textScaleFactor = MediaQuery.textScaleFactorOf(context); final useTvLayout = settings.useTvLayout; - return Selector( - selector: (context, mq) => mq.padding.top, - builder: (context, mqPaddingTop, child) { - return SliverPersistentHeader( - floating: !useTvLayout, - pinned: pinned, - delegate: _SliverAppBarDelegate( - height: mqPaddingTop + appBarHeightForContentHeight(contentHeight), - child: child!, - ), - ); - }, - child: DirectionalSafeArea( - start: !useTvLayout, - bottom: false, - child: AvesFloatingBar( - builder: (context, backgroundColor, child) => Material( - color: backgroundColor, - child: child, - ), - child: Column( - children: [ - SizedBox( - height: kToolbarHeight * context.select((mq) => mq.textScaleFactor), - child: Row( - children: [ - leading != null - ? Padding( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: Hero( - tag: leadingHeroTag, - flightShuttleBuilder: _flightShuttleBuilder, - transitionOnUserGestures: true, - child: FontSizeIconTheme( - child: leading!, + return SliverPersistentHeader( + floating: !useTvLayout, + pinned: pinned, + delegate: _SliverAppBarDelegate( + height: MediaQuery.paddingOf(context).top + appBarHeightForContentHeight(contentHeight), + child: DirectionalSafeArea( + start: !useTvLayout, + bottom: false, + child: AvesFloatingBar( + builder: (context, backgroundColor, child) => Material( + color: backgroundColor, + child: child, + ), + child: Column( + children: [ + SizedBox( + height: kToolbarHeight * textScaleFactor, + child: Row( + children: [ + leading != null + ? Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: Hero( + tag: leadingHeroTag, + flightShuttleBuilder: _flightShuttleBuilder, + transitionOnUserGestures: true, + child: FontSizeIconTheme( + child: leading!, + ), ), - ), - ) - : const SizedBox(width: 16), - Expanded( - child: DefaultTextStyle( - style: Theme.of(context).appBarTheme.titleTextStyle!, - child: Hero( - tag: titleHeroTag, - flightShuttleBuilder: _flightShuttleBuilder, - transitionOnUserGestures: true, - child: AnimatedSwitcher( - duration: context.read().iconAnimation, - child: FontSizeIconTheme( - child: Row( - key: ValueKey(transitionKey), - children: [ - Expanded(child: title), - ...actions, - ], + ) + : const SizedBox(width: 16), + Expanded( + child: DefaultTextStyle( + style: Theme.of(context).appBarTheme.titleTextStyle!, + child: Hero( + tag: titleHeroTag, + flightShuttleBuilder: _flightShuttleBuilder, + transitionOnUserGestures: true, + child: AnimatedSwitcher( + duration: context.read().iconAnimation, + child: FontSizeIconTheme( + child: Row( + key: ValueKey(transitionKey), + children: [ + Expanded(child: title), + ...actions, + ], + ), ), ), ), ), ), - ), - ], + ], + ), ), - ), - if (bottom != null) bottom!, - ], + if (bottom != null) bottom!, + ], + ), ), ), ), diff --git a/lib/widgets/common/identity/aves_filter_chip.dart b/lib/widgets/common/identity/aves_filter_chip.dart index 87809c610..1192c6322 100644 --- a/lib/widgets/common/identity/aves_filter_chip.dart +++ b/lib/widgets/common/identity/aves_filter_chip.dart @@ -31,12 +31,12 @@ enum HeroType { always, onTap, never } @immutable class AvesFilterDecoration { - final Widget widget; final Radius radius; + final Widget widget; const AvesFilterDecoration({ - required this.widget, required this.radius, + required this.widget, }); BorderRadius get textBorderRadius => BorderRadius.vertical(bottom: radius); @@ -88,9 +88,9 @@ class AvesFilterChip extends StatefulWidget { required double chipPadding, required double rowPadding, }) { - return context.select((mq) { - return (mq.size.width - mq.padding.horizontal - chipPadding * minChipPerRow - rowPadding) / minChipPerRow; - }); + final mqWidth = MediaQuery.sizeOf(context).width; + final mqHorizontalPadding = MediaQuery.paddingOf(context).horizontal; + return (mqWidth - mqHorizontalPadding - chipPadding * minChipPerRow - rowPadding) / minChipPerRow; } static Future showDefaultLongPressMenu(BuildContext context, CollectionFilter filter, Offset tapPosition) async { diff --git a/lib/widgets/common/identity/aves_logo.dart b/lib/widgets/common/identity/aves_logo.dart index c6c03d914..cd25fe4d5 100644 --- a/lib/widgets/common/identity/aves_logo.dart +++ b/lib/widgets/common/identity/aves_logo.dart @@ -37,7 +37,7 @@ class AvesLogo extends StatelessWidget { radius: size / 2, child: CircleAvatar( backgroundColor: Colors.white, - radius: size / 2 - AvesBorder.curvedBorderWidth, + radius: size / 2 - AvesBorder.curvedBorderWidth(context), child: Padding( padding: EdgeInsets.only(top: size / 15), child: child, diff --git a/lib/widgets/common/identity/buttons/overlay_button.dart b/lib/widgets/common/identity/buttons/overlay_button.dart index d4b085433..ce1aeeffd 100644 --- a/lib/widgets/common/identity/buttons/overlay_button.dart +++ b/lib/widgets/common/identity/buttons/overlay_button.dart @@ -73,7 +73,7 @@ class _OverlayButtonState extends State { builder: (context, focused, child) { final border = AvesBorder.border( context, - width: AvesBorder.curvedBorderWidth * (focused ? 3 : 1), + width: AvesBorder.curvedBorderWidth(context) * (focused ? 3 : 1), ); return borderRadius != null ? BlurredRRect( diff --git a/lib/widgets/common/map/buttons/panel.dart b/lib/widgets/common/map/buttons/panel.dart index ecb325cd9..c25427800 100644 --- a/lib/widgets/common/map/buttons/panel.dart +++ b/lib/widgets/common/map/buttons/panel.dart @@ -42,7 +42,6 @@ class MapButtonPanel extends StatelessWidget { tooltip: MaterialLocalizations.of(context).backButtonTooltip, ); } - break; case MapNavigationButton.map: if (openMapPage != null) { navigationButton = MapOverlayButton( @@ -51,7 +50,6 @@ class MapButtonPanel extends StatelessWidget { tooltip: context.l10n.openMapPageTooltip, ); } - break; case MapNavigationButton.none: break; } diff --git a/lib/widgets/common/map/geo_map.dart b/lib/widgets/common/map/geo_map.dart index 5d008fb20..ef12052ea 100644 --- a/lib/widgets/common/map/geo_map.dart +++ b/lib/widgets/common/map/geo_map.dart @@ -1,6 +1,5 @@ import 'dart:async'; import 'dart:math'; -import 'dart:ui'; import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/extensions/images.dart'; @@ -35,6 +34,7 @@ class GeoMap extends StatefulWidget { final AvesMapController? controller; final Listenable? collectionListenable; final List entries; + final Size availableSize; final LatLng? initialCenter; final ValueNotifier isAnimatingNotifier; final ValueNotifier? dotLocationNotifier; @@ -60,6 +60,7 @@ class GeoMap extends StatefulWidget { this.controller, this.collectionListenable, required this.entries, + required this.availableSize, this.initialCenter, required this.isAnimatingNotifier, this.dotLocationNotifier, @@ -133,6 +134,13 @@ class _GeoMapState extends State { @override Widget build(BuildContext context) { + final devicePixelRatio = View.of(context).devicePixelRatio; + void onMarkerLongPress(GeoEntry geoEntry, LatLng tapLocation) => _onMarkerLongPress( + geoEntry: geoEntry, + tapLocation: tapLocation, + devicePixelRatio: devicePixelRatio, + ); + return Selector( selector: (context, s) => s.mapStyle, builder: (context, mapStyle, child) { @@ -143,6 +151,7 @@ class _GeoMapState extends State { buildThumbnailImage: (extent) => ThumbnailImage( entry: key.entry, extent: extent, + devicePixelRatio: devicePixelRatio, progressive: !isHeavy, ), ); @@ -173,9 +182,8 @@ class _GeoMapState extends State { onUserZoomChange: widget.onUserZoomChange, onMapTap: widget.onMapTap, onMarkerTap: _onMarkerTap, - onMarkerLongPress: _onMarkerLongPress, + onMarkerLongPress: onMarkerLongPress, ); - break; case EntryMapStyle.osmHot: case EntryMapStyle.stamenToner: case EntryMapStyle.stamenWatercolor: @@ -204,9 +212,8 @@ class _GeoMapState extends State { onUserZoomChange: widget.onUserZoomChange, onMapTap: widget.onMapTap, onMarkerTap: _onMarkerTap, - onMarkerLongPress: _onMarkerLongPress, + onMarkerLongPress: onMarkerLongPress, ); - break; } } else { final overlay = Center( @@ -360,8 +367,8 @@ class _GeoMapState extends State { ); bounds = bounds.copyWith(zoom: max(minInitialZoom, bounds.zoom.floorToDouble())); - final availableSize = window.physicalSize / window.devicePixelRatio; final neededSize = bounds.toDisplaySize(); + final availableSize = widget.availableSize; if (neededSize.width > availableSize.width || neededSize.height > availableSize.height) { return _initBoundsForEntries(entries: entries, recentCount: (recentCount ?? 10000) ~/ 10); } @@ -457,7 +464,11 @@ class _GeoMapState extends State { onTap(markerLocation, markerEntry); } - Future _onMarkerLongPress(GeoEntry geoEntry, LatLng tapLocation) async { + Future _onMarkerLongPress({ + required GeoEntry geoEntry, + required LatLng tapLocation, + required double devicePixelRatio, + }) async { final onMarkerLongPress = widget.onMarkerLongPress; if (onMarkerLongPress == null) return; @@ -478,6 +489,7 @@ class _GeoMapState extends State { buildThumbnailImage: (extent) => ThumbnailImage( entry: markerEntry, extent: extent, + devicePixelRatio: devicePixelRatio, ), ); onMarkerLongPress( diff --git a/lib/widgets/common/map/leaflet/scale_layer.dart b/lib/widgets/common/map/leaflet/scale_layer.dart index af99267d9..d9c55d1db 100644 --- a/lib/widgets/common/map/leaflet/scale_layer.dart +++ b/lib/widgets/common/map/leaflet/scale_layer.dart @@ -82,7 +82,6 @@ class ScaleLayerWidget extends StatelessWidget { // meters distanceMeters = scaleMeters[scaleLevel]; displayDistance = distanceMeters >= metersInAKilometer ? '${(distanceMeters / metersInAKilometer).toStringAsFixed(0)} km' : '${distanceMeters.toStringAsFixed(0)} m'; - break; case UnitSystem.imperial: if (scaleLevel < 15) { // miles @@ -95,7 +94,6 @@ class ScaleLayerWidget extends StatelessWidget { distanceMeters = distanceFeet * metersInAFoot; displayDistance = '${distanceFeet.toStringAsFixed(0)} ft'; } - break; } final start = map.project(center); diff --git a/lib/widgets/common/map/leaflet/tile_layers.dart b/lib/widgets/common/map/leaflet/tile_layers.dart index 5ea7d1bb9..2a547a0a3 100644 --- a/lib/widgets/common/map/leaflet/tile_layers.dart +++ b/lib/widgets/common/map/leaflet/tile_layers.dart @@ -1,7 +1,6 @@ import 'package:aves/model/device.dart'; import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; -import 'package:provider/provider.dart'; const _tileLayerBackgroundColor = Colors.transparent; @@ -14,7 +13,7 @@ class OSMHotLayer extends StatelessWidget { urlTemplate: 'https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png', subdomains: const ['a', 'b', 'c'], backgroundColor: _tileLayerBackgroundColor, - retinaMode: context.select((mq) => mq.devicePixelRatio) > 1, + retinaMode: MediaQuery.devicePixelRatioOf(context) > 1, userAgentPackageName: device.userAgent, ); } @@ -29,7 +28,7 @@ class StamenTonerLayer extends StatelessWidget { urlTemplate: 'https://stamen-tiles-{s}.a.ssl.fastly.net/toner-lite/{z}/{x}/{y}{r}.png', subdomains: const ['a', 'b', 'c', 'd'], backgroundColor: _tileLayerBackgroundColor, - retinaMode: context.select((mq) => mq.devicePixelRatio) > 1, + retinaMode: MediaQuery.devicePixelRatioOf(context) > 1, userAgentPackageName: device.userAgent, ); } @@ -44,7 +43,7 @@ class StamenWatercolorLayer extends StatelessWidget { urlTemplate: 'https://stamen-tiles-{s}.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.jpg', subdomains: const ['a', 'b', 'c', 'd'], backgroundColor: _tileLayerBackgroundColor, - retinaMode: context.select((mq) => mq.devicePixelRatio) > 1, + retinaMode: MediaQuery.devicePixelRatioOf(context) > 1, userAgentPackageName: device.userAgent, ); } diff --git a/lib/widgets/common/map/map_action_delegate.dart b/lib/widgets/common/map/map_action_delegate.dart index e108d01bb..d5fe1b079 100644 --- a/lib/widgets/common/map/map_action_delegate.dart +++ b/lib/widgets/common/map/map_action_delegate.dart @@ -26,13 +26,10 @@ class MapActionDelegate { ), onSelection: (v) => settings.mapStyle = v, ); - break; case MapAction.zoomIn: controller?.zoomBy(1); - break; case MapAction.zoomOut: controller?.zoomBy(-1); - break; } } } diff --git a/lib/widgets/common/search/page.dart b/lib/widgets/common/search/page.dart index 31bfca47d..8aee4f8d9 100644 --- a/lib/widgets/common/search/page.dart +++ b/lib/widgets/common/search/page.dart @@ -114,13 +114,11 @@ class _SearchPageState extends State { key: const ValueKey(SearchBody.suggestions), child: widget.delegate.buildSuggestions(context), ); - break; case SearchBody.results: body = KeyedSubtree( key: const ValueKey(SearchBody.results), child: widget.delegate.buildResults(context), ); - break; case null: break; } diff --git a/lib/widgets/common/thumbnail/decorated.dart b/lib/widgets/common/thumbnail/decorated.dart index b4176b264..e55912e26 100644 --- a/lib/widgets/common/thumbnail/decorated.dart +++ b/lib/widgets/common/thumbnail/decorated.dart @@ -14,7 +14,8 @@ class DecoratedThumbnail extends StatelessWidget { final Object? Function()? heroTagger; static final Color borderColor = Colors.grey.shade700; - static final double borderWidth = AvesBorder.straightBorderWidth; + + static double borderWidth(BuildContext context) => AvesBorder.straightBorderWidth(context); const DecoratedThumbnail({ super.key, @@ -35,6 +36,7 @@ class DecoratedThumbnail extends StatelessWidget { Widget child = ThumbnailImage( entry: entry, extent: tileExtent, + devicePixelRatio: MediaQuery.devicePixelRatioOf(context), isMosaic: isMosaic, cancellableNotifier: cancellableNotifier, heroTag: heroTagger?.call(), @@ -64,7 +66,7 @@ class DecoratedThumbnail extends StatelessWidget { foregroundDecoration: BoxDecoration( border: Border.fromBorderSide(BorderSide( color: borderColor, - width: borderWidth, + width: borderWidth(context), )), ), width: thumbnailWidth, diff --git a/lib/widgets/common/thumbnail/image.dart b/lib/widgets/common/thumbnail/image.dart index 0c3464951..794979ef8 100644 --- a/lib/widgets/common/thumbnail/image.dart +++ b/lib/widgets/common/thumbnail/image.dart @@ -1,5 +1,4 @@ import 'dart:math'; -import 'dart:ui'; import 'package:aves/image_providers/thumbnail_provider.dart'; import 'package:aves/model/entry/entry.dart'; @@ -20,7 +19,7 @@ import 'package:provider/provider.dart'; class ThumbnailImage extends StatefulWidget { final AvesEntry entry; - final double extent; + final double extent, devicePixelRatio; final bool isMosaic, progressive; final BoxFit? fit; final bool showLoadingBackground; @@ -31,6 +30,7 @@ class ThumbnailImage extends StatefulWidget { super.key, required this.entry, required this.extent, + required this.devicePixelRatio, this.progressive = true, this.isMosaic = false, this.fit, @@ -57,7 +57,6 @@ class _ThumbnailImageState extends State { ImageInfo? _lastImageInfo; Object? _lastException; late final ImageStreamListener _streamListener; - late DisposableBuildContext> _scrollAwareContext; AvesEntry get entry => widget.entry; @@ -69,7 +68,6 @@ class _ThumbnailImageState extends State { void initState() { super.initState(); _streamListener = ImageStreamListener(_onImageLoad, onError: _onError); - _scrollAwareContext = DisposableBuildContext>(this); _registerWidget(widget); } @@ -85,7 +83,6 @@ class _ThumbnailImageState extends State { @override void dispose() { _unregisterWidget(widget); - _scrollAwareContext.dispose(); super.dispose(); } @@ -126,16 +123,10 @@ class _ThumbnailImageState extends State { _providers.addAll([ if (lowQuality != null) _ConditionalImageProvider( - ScrollAwareImageProvider( - context: _scrollAwareContext, - imageProvider: lowQuality, - ), + lowQuality, ), _ConditionalImageProvider( - ScrollAwareImageProvider( - context: _scrollAwareContext, - imageProvider: highQuality, - ), + highQuality, _needSizedProvider, ), ]); @@ -176,8 +167,7 @@ class _ThumbnailImageState extends State { bool _needSizedProvider(ImageInfo? currentImageInfo) { if (currentImageInfo == null) return true; final currentImage = currentImageInfo.image; - // directly uses `devicePixelRatio` as it never changes, to avoid visiting ancestors via `MediaQuery` - final sizedThreshold = extent * window.devicePixelRatio; + final sizedThreshold = extent * widget.devicePixelRatio; return sizedThreshold > min(currentImage.width, currentImage.height); } diff --git a/lib/widgets/debug/app_debug_page.dart b/lib/widgets/debug/app_debug_page.dart index 70729be0d..9c5506efb 100644 --- a/lib/widgets/debug/app_debug_page.dart +++ b/lib/widgets/debug/app_debug_page.dart @@ -182,18 +182,15 @@ class _AppDebugPageState extends State { }, false); await favourites.clear(); await favourites.add(source.visibleEntries); - break; case AppDebugAction.prepScreenshotStats: settings.changeFilterVisibility(settings.hiddenFilters, true); settings.changeFilterVisibility({ PathFilter('/storage/emulated/0/Pictures/Dev'), }, false); - break; case AppDebugAction.prepScreenshotCountries: settings.changeFilterVisibility({ LocationFilter(LocationLevel.country, 'Belgium;BE'), }, false); - break; case AppDebugAction.mediaStoreScanDir: // scan files copied from test assets // we do it via the app instead of broadcasting via ADB @@ -202,7 +199,6 @@ class _AppDebugPageState extends State { context: context, builder: (context) => const MediaStoreScanDirDialog(), ); - break; case AppDebugAction.greenScreen: await Navigator.maybeOf(context)?.push( MaterialPageRoute( @@ -212,7 +208,6 @@ class _AppDebugPageState extends State { ), ), ); - break; } } } diff --git a/lib/widgets/debug/settings.dart b/lib/widgets/debug/settings.dart index b03b03356..cbc87137c 100644 --- a/lib/widgets/debug/settings.dart +++ b/lib/widgets/debug/settings.dart @@ -66,7 +66,7 @@ class DebugSettingsSection extends StatelessWidget { 'recentDestinationAlbums': toMultiline(settings.recentDestinationAlbums), 'recentTags': toMultiline(settings.recentTags), 'locale': '${settings.locale}', - 'systemLocales': '${WidgetsBinding.instance.window.locales}', + 'systemLocales': '${WidgetsBinding.instance.platformDispatcher.locales}', 'topEntryIds': '${settings.topEntryIds}', }, ), diff --git a/lib/widgets/dialogs/add_shortcut_dialog.dart b/lib/widgets/dialogs/add_shortcut_dialog.dart index f445e3df6..095a836b8 100644 --- a/lib/widgets/dialogs/add_shortcut_dialog.dart +++ b/lib/widgets/dialogs/add_shortcut_dialog.dart @@ -8,7 +8,6 @@ import 'package:aves/widgets/dialogs/item_picker.dart'; import 'package:aves/widgets/dialogs/pick_dialogs/item_pick_page.dart'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; import 'package:tuple/tuple.dart'; import 'aves_dialog.dart'; @@ -60,7 +59,7 @@ class _AddShortcutDialogState extends State { return MediaQueryDataProvider( child: Builder( builder: (context) { - final shortestSide = context.select((mq) => mq.size.shortestSide); + final shortestSide = MediaQuery.sizeOf(context).shortestSide; final extent = (shortestSide / 3.0).clamp(60.0, 160.0); return AvesDialog( scrollableContent: [ diff --git a/lib/widgets/dialogs/aves_confirmation_dialog.dart b/lib/widgets/dialogs/aves_confirmation_dialog.dart index ffbba695d..dd9f4b312 100644 --- a/lib/widgets/dialogs/aves_confirmation_dialog.dart +++ b/lib/widgets/dialogs/aves_confirmation_dialog.dart @@ -72,16 +72,12 @@ void _skipConfirmation(ConfirmationDialog type) { switch (type) { case ConfirmationDialog.createVault: settings.confirmCreateVault = false; - break; case ConfirmationDialog.deleteForever: settings.confirmDeleteForever = false; - break; case ConfirmationDialog.moveToBin: settings.confirmMoveToBin = false; - break; case ConfirmationDialog.moveUndatedItems: settings.confirmMoveUndatedItems = false; - break; } } diff --git a/lib/widgets/dialogs/aves_dialog.dart b/lib/widgets/dialogs/aves_dialog.dart index c19c0fe5a..8127a61d1 100644 --- a/lib/widgets/dialogs/aves_dialog.dart +++ b/lib/widgets/dialogs/aves_dialog.dart @@ -3,7 +3,6 @@ import 'dart:ui'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; class AvesDialog extends StatelessWidget { static const confirmationRouteName = '/dialog/confirmation'; @@ -104,7 +103,7 @@ class AvesDialog extends StatelessWidget { // workaround because the dialog tries // to size itself to the content intrinsic size, // but the `ListView` viewport does not have one - width: context.select((mq) => mq.size.width / 2), + width: MediaQuery.sizeOf(context).width / 2, child: DecoratedBox( decoration: contentDecoration(context), child: child, diff --git a/lib/widgets/dialogs/convert_entry_dialog.dart b/lib/widgets/dialogs/convert_entry_dialog.dart index f7b047d17..fe9bae4a0 100644 --- a/lib/widgets/dialogs/convert_entry_dialog.dart +++ b/lib/widgets/dialogs/convert_entry_dialog.dart @@ -78,11 +78,9 @@ class _ConvertEntryDialogState extends State { final displaySize = entries.first.displaySize; _widthController.text = '${displaySize.width.round()}'; _heightController.text = '${displaySize.height.round()}'; - break; case LengthUnit.percent: _widthController.text = '100'; _heightController.text = '100'; - break; } } @@ -149,10 +147,8 @@ class _ConvertEntryDialogState extends State { switch (_lengthUnit) { case LengthUnit.px: _heightController.text = '${(width / entries.first.displayAspectRatio).round()}'; - break; case LengthUnit.percent: _heightController.text = '$width'; - break; } } else { _heightController.text = ''; @@ -175,10 +171,8 @@ class _ConvertEntryDialogState extends State { switch (_lengthUnit) { case LengthUnit.px: _widthController.text = '${(height * entries.first.displayAspectRatio).round()}'; - break; case LengthUnit.percent: _widthController.text = '$height'; - break; } } else { _widthController.text = ''; diff --git a/lib/widgets/dialogs/entry_editors/edit_date_dialog.dart b/lib/widgets/dialogs/entry_editors/edit_date_dialog.dart index f46d2c53c..4111ed044 100644 --- a/lib/widgets/dialogs/entry_editors/edit_date_dialog.dart +++ b/lib/widgets/dialogs/entry_editors/edit_date_dialog.dart @@ -145,7 +145,7 @@ class _EditEntryDateDialogState extends State { Widget _buildSetCustomContent(BuildContext context) { final l10n = context.l10n; final locale = l10n.localeName; - final use24hour = context.select((v) => v.alwaysUse24HourFormat); + final use24hour = MediaQuery.alwaysUse24HourFormatOf(context); return Padding( padding: const EdgeInsetsDirectional.only(start: 16, end: 8), @@ -179,7 +179,7 @@ class _EditEntryDateDialogState extends State { Widget _buildCopyItemContent(BuildContext context) { final l10n = context.l10n; final locale = l10n.localeName; - final use24hour = context.select((v) => v.alwaysUse24HourFormat); + final use24hour = MediaQuery.alwaysUse24HourFormatOf(context); return Padding( padding: const EdgeInsetsDirectional.only(start: 16, end: 8), @@ -365,11 +365,9 @@ class _EditEntryDateDialogState extends State { case DateEditAction.copyItem: case DateEditAction.extractFromTitle: _isValidNotifier.value = true; - break; case DateEditAction.shift: case DateEditAction.remove: _isValidNotifier.value = _fields.isNotEmpty; - break; } } diff --git a/lib/widgets/dialogs/entry_editors/edit_location_dialog.dart b/lib/widgets/dialogs/entry_editors/edit_location_dialog.dart index 4096bd154..1cad7d7e4 100644 --- a/lib/widgets/dialogs/entry_editors/edit_location_dialog.dart +++ b/lib/widgets/dialogs/entry_editors/edit_location_dialog.dart @@ -307,33 +307,26 @@ class _EditEntryLocationDialogState extends State { switch (_action) { case LocationEditAction.chooseOnMap: _isValidNotifier.value = _mapCoordinates != null; - break; case LocationEditAction.copyItem: _isValidNotifier.value = _copyItemSource.hasGps; - break; case LocationEditAction.setCustom: _isValidNotifier.value = _parseLatLng() != null; - break; case LocationEditAction.remove: _isValidNotifier.value = true; - break; } } void _submit(BuildContext context) { + final navigator = Navigator.maybeOf(context); switch (_action) { case LocationEditAction.chooseOnMap: - Navigator.maybeOf(context)?.pop(_mapCoordinates); - break; + navigator?.pop(_mapCoordinates); case LocationEditAction.copyItem: - Navigator.maybeOf(context)?.pop(_copyItemSource.latLng); - break; + navigator?.pop(_copyItemSource.latLng); case LocationEditAction.setCustom: - Navigator.maybeOf(context)?.pop(_parseLatLng()); - break; + navigator?.pop(_parseLatLng()); case LocationEditAction.remove: - Navigator.maybeOf(context)?.pop(ExtraAvesEntryMetadataEdition.removalLocation); - break; + navigator?.pop(ExtraAvesEntryMetadataEdition.removalLocation); } } } diff --git a/lib/widgets/dialogs/entry_editors/edit_rating_dialog.dart b/lib/widgets/dialogs/entry_editors/edit_rating_dialog.dart index 8004d5e1c..4c79a3fb9 100644 --- a/lib/widgets/dialogs/entry_editors/edit_rating_dialog.dart +++ b/lib/widgets/dialogs/entry_editors/edit_rating_dialog.dart @@ -32,11 +32,9 @@ class _EditEntryRatingDialogState extends State { case -1: _action = _RatingAction.rejected; _rating = 0; - break; case 0: _action = _RatingAction.unrated; _rating = 0; - break; default: _action = _RatingAction.set; _rating = entryRating; @@ -121,13 +119,10 @@ class _EditEntryRatingDialogState extends State { switch (_action) { case _RatingAction.set: entryRating = _rating; - break; case _RatingAction.rejected: entryRating = -1; - break; case _RatingAction.unrated: entryRating = 0; - break; } Navigator.maybeOf(context)?.pop(entryRating); } diff --git a/lib/widgets/dialogs/entry_editors/rename_entry_set_page.dart b/lib/widgets/dialogs/entry_editors/rename_entry_set_page.dart index 3ba035090..a9bb11799 100644 --- a/lib/widgets/dialogs/entry_editors/rename_entry_set_page.dart +++ b/lib/widgets/dialogs/entry_editors/rename_entry_set_page.dart @@ -16,7 +16,6 @@ import 'package:aves/widgets/common/identity/buttons/outlined_button.dart'; import 'package:aves/widgets/common/thumbnail/decorated.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; -import 'package:provider/provider.dart'; class RenameEntrySetPage extends StatefulWidget { static const routeName = '/rename_entry_set'; @@ -60,6 +59,8 @@ class _RenameEntrySetPageState extends State { @override Widget build(BuildContext context) { final l10n = context.l10n; + final textScaleFactor = MediaQuery.textScaleFactorOf(context); + final effectiveThumbnailExtent = max(thumbnailExtent, thumbnailExtent * textScaleFactor); return AvesScaffold( appBar: AppBar( title: Text(l10n.renameEntrySetPageTitle), @@ -121,63 +122,58 @@ class _RenameEntrySetPageState extends State { ), ), Expanded( - child: Selector( - selector: (context, mq) => mq.textScaleFactor, - builder: (context, textScaleFactor, child) { - final effectiveThumbnailExtent = max(thumbnailExtent, thumbnailExtent * textScaleFactor); - return GridTheme( - extent: effectiveThumbnailExtent, - child: ListView.separated( - physics: const NeverScrollableScrollPhysics(), - padding: const EdgeInsets.symmetric(vertical: 12), - itemBuilder: (context, index) { - final entry = entries[index]; - final sourceName = entry.filenameWithoutExtension ?? ''; - return Row( + child: GridTheme( + extent: effectiveThumbnailExtent, + child: ListView.separated( + physics: const NeverScrollableScrollPhysics(), + padding: const EdgeInsets.symmetric(vertical: 12), + itemBuilder: (context, index) { + final entry = entries[index]; + final sourceName = entry.filenameWithoutExtension ?? ''; + return Row( + children: [ + DecoratedThumbnail( + entry: entry, + tileExtent: effectiveThumbnailExtent, + selectable: false, + highlightable: false, + ), + const SizedBox(width: 8), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - DecoratedThumbnail( - entry: entry, - tileExtent: effectiveThumbnailExtent, - selectable: false, - highlightable: false, + Text( + sourceName, + style: TextStyle(color: Theme.of(context).textTheme.bodySmall!.color), + softWrap: false, + overflow: TextOverflow.fade, + maxLines: 1, ), - const SizedBox(width: 8), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - sourceName, - style: TextStyle(color: Theme.of(context).textTheme.bodySmall!.color), - softWrap: false, - overflow: TextOverflow.fade, - maxLines: 1, - ), - const SizedBox(height: 4), - ValueListenableBuilder( - valueListenable: _namingPatternNotifier, - builder: (context, pattern, child) { - return Text( - pattern.apply(entry, index), - softWrap: false, - overflow: TextOverflow.fade, - maxLines: 1, - ); - }, - ), - ], - ), + const SizedBox(height: 4), + ValueListenableBuilder( + valueListenable: _namingPatternNotifier, + builder: (context, pattern, child) { + return Text( + pattern.apply(entry, index), + softWrap: false, + overflow: TextOverflow.fade, + maxLines: 1, + ); + }, ), ], - ); - }, - separatorBuilder: (context, index) => const SizedBox( - height: CollectionGrid.fixedExtentLayoutSpacing, + ), ), - itemCount: min(entryCount, previewMax), - ), + ], ); - }), + }, + separatorBuilder: (context, index) => const SizedBox( + height: CollectionGrid.fixedExtentLayoutSpacing, + ), + itemCount: min(entryCount, previewMax), + ), + ), ), const Divider(height: 0), Center( diff --git a/lib/widgets/dialogs/filter_editors/cover_selection_dialog.dart b/lib/widgets/dialogs/filter_editors/cover_selection_dialog.dart index dbd30f69d..3a3c8db7d 100644 --- a/lib/widgets/dialogs/filter_editors/cover_selection_dialog.dart +++ b/lib/widgets/dialogs/filter_editors/cover_selection_dialog.dart @@ -57,7 +57,10 @@ class _CoverSelectionDialogState extends State { static const double itemPickerExtent = 46; static const double appPickerExtent = 32; - double tabBarHeight(BuildContext context) => 64 * max(1, MediaQuery.textScaleFactorOf(context)); + double tabBarHeight(BuildContext context) { + final textScaleFactor = MediaQuery.textScaleFactorOf(context); + return 64 * max(1, textScaleFactor); + } static const double tabIndicatorWeight = 2; diff --git a/lib/widgets/dialogs/filter_editors/pattern_dialog.dart b/lib/widgets/dialogs/filter_editors/pattern_dialog.dart index f77241c9d..d027af570 100644 --- a/lib/widgets/dialogs/filter_editors/pattern_dialog.dart +++ b/lib/widgets/dialogs/filter_editors/pattern_dialog.dart @@ -2,7 +2,6 @@ import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/dialogs/aves_dialog.dart'; import 'package:flutter/material.dart'; import 'package:pattern_lock/pattern_lock.dart'; -import 'package:provider/provider.dart'; class PatternDialog extends StatefulWidget { static const routeName = '/dialog/pattern'; @@ -33,7 +32,7 @@ class _PatternDialogState extends State { Padding( padding: const EdgeInsets.symmetric(vertical: 16), child: SizedBox.square( - dimension: context.select((mq) => mq.size.shortestSide / 2), + dimension: MediaQuery.sizeOf(context).shortestSide / 2, child: PatternLock( relativePadding: .4, selectedColor: colorScheme.secondary, diff --git a/lib/widgets/dialogs/item_picker.dart b/lib/widgets/dialogs/item_picker.dart index 1b908c1f0..c13726e17 100644 --- a/lib/widgets/dialogs/item_picker.dart +++ b/lib/widgets/dialogs/item_picker.dart @@ -43,6 +43,7 @@ class ItemPicker extends StatelessWidget { ThumbnailImage( entry: entry, extent: extent, + devicePixelRatio: MediaQuery.devicePixelRatioOf(context), ), PositionedDirectional( end: -1, diff --git a/lib/widgets/dialogs/pick_dialogs/album_pick_page.dart b/lib/widgets/dialogs/pick_dialogs/album_pick_page.dart index 253e1ebaa..13f446aef 100644 --- a/lib/widgets/dialogs/pick_dialogs/album_pick_page.dart +++ b/lib/widgets/dialogs/pick_dialogs/album_pick_page.dart @@ -164,13 +164,10 @@ class _AlbumPickPageState extends State<_AlbumPickPage> { switch (action) { case ChipSetAction.createAlbum: _createAlbum(); - break; case ChipSetAction.createVault: _createVault(); - break; default: actionDelegate.onActionSelected(context, {}, action); - break; } } diff --git a/lib/widgets/dialogs/pick_dialogs/location_pick_page.dart b/lib/widgets/dialogs/pick_dialogs/location_pick_page.dart index a20f76ad4..5c79bc06a 100644 --- a/lib/widgets/dialogs/pick_dialogs/location_pick_page.dart +++ b/lib/widgets/dialogs/pick_dialogs/location_pick_page.dart @@ -20,7 +20,6 @@ import 'package:aves_map/aves_map.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:latlong2/latlong.dart'; -import 'package:provider/provider.dart'; class LocationPickPage extends StatelessWidget { static const routeName = '/location_pick'; @@ -137,6 +136,7 @@ class _ContentState extends State<_Content> with SingleTickerProviderStateMixin controller: _mapController, collectionListenable: openingCollection, entries: openingCollection?.sortedEntries ?? [], + availableSize: MediaQuery.sizeOf(context), initialCenter: widget.initialLocation, isAnimatingNotifier: _isPageAnimatingNotifier, dotLocationNotifier: _dotLocationNotifier, @@ -177,12 +177,11 @@ class _LocationInfo extends StatelessWidget { @override Widget build(BuildContext context) { - final orientation = context.select((v) => v.orientation); - return ValueListenableBuilder( valueListenable: locationNotifier, builder: (context, location, child) { - final content = orientation == Orientation.portrait + final isPortrait = MediaQuery.orientationOf(context) == Orientation.portrait; + final content = isPortrait ? [ Expanded( child: Column( @@ -218,7 +217,10 @@ class _LocationInfo extends StatelessWidget { ); } - static double getIconSize(BuildContext context) => 16.0 * context.select((mq) => mq.textScaleFactor); + static double getIconSize(BuildContext context) { + final textScaleFactor = MediaQuery.textScaleFactorOf(context); + return 16 * textScaleFactor; + } } class _AddressRow extends StatefulWidget { @@ -251,6 +253,7 @@ class _AddressRowState extends State<_AddressRow> { @override Widget build(BuildContext context) { + final textScaleFactor = MediaQuery.textScaleFactorOf(context); return Row( mainAxisSize: MainAxisSize.min, children: [ @@ -263,7 +266,7 @@ class _AddressRowState extends State<_AddressRow> { // addresses can include non-latin scripts with inconsistent line height, // which is especially an issue for relayout/painting of heavy Google map, // so we give extra height to give breathing room to the text and stabilize layout - height: Theme.of(context).textTheme.bodyMedium!.fontSize! * context.select((mq) => mq.textScaleFactor) * 2, + height: Theme.of(context).textTheme.bodyMedium!.fontSize! * textScaleFactor * 2, child: ValueListenableBuilder( valueListenable: _addressLineNotifier, builder: (context, addressLine, child) { diff --git a/lib/widgets/dialogs/tile_view_dialog.dart b/lib/widgets/dialogs/tile_view_dialog.dart index b4aa1d56e..941963e98 100644 --- a/lib/widgets/dialogs/tile_view_dialog.dart +++ b/lib/widgets/dialogs/tile_view_dialog.dart @@ -195,8 +195,9 @@ class _TileViewDialogState extends State> with dropdownColor: Themes.thirdLayerColor(context), ); - final iconSize = IconTheme.of(context).size! * MediaQuery.textScaleFactorOf(context); - final isPortrait = context.select((mq) => mq.orientation) == Orientation.portrait; + final textScaleFactor = MediaQuery.textScaleFactorOf(context); + final iconSize = IconTheme.of(context).size! * textScaleFactor; + final isPortrait = MediaQuery.orientationOf(context) == Orientation.portrait; final child = isPortrait ? Column( mainAxisSize: MainAxisSize.min, diff --git a/lib/widgets/filter_grids/albums_page.dart b/lib/widgets/filter_grids/albums_page.dart index 259fa0728..a40a79a66 100644 --- a/lib/widgets/filter_grids/albums_page.dart +++ b/lib/widgets/filter_grids/albums_page.dart @@ -123,7 +123,6 @@ class AlbumListPage extends StatelessWidget { if (sections.containsKey(vaultKey)) vaultKey: sections[vaultKey]!, if (sections.containsKey(regularKey)) regularKey: sections[regularKey]!, }; - break; case AlbumChipGroupFactor.mimeType: final visibleEntries = source.visibleEntries; sections = groupBy, ChipSectionKey>(unpinnedMapEntries, (kv) { @@ -134,12 +133,10 @@ class AlbumListPage extends StatelessWidget { if (!hasImage && hasVideo) return MimeTypeSectionKey.videos(context); return MimeTypeSectionKey.mixed(context); }); - break; case AlbumChipGroupFactor.volume: sections = groupBy, ChipSectionKey>(unpinnedMapEntries, (kv) { return StorageVolumeSectionKey(context, androidFileUtils.getStorageVolume(kv.filter.album)); }); - break; case AlbumChipGroupFactor.none: return { if (sortedMapEntries.isNotEmpty) diff --git a/lib/widgets/filter_grids/common/action_delegates/album_set.dart b/lib/widgets/filter_grids/common/action_delegates/album_set.dart index 7c46687a4..28894923c 100644 --- a/lib/widgets/filter_grids/common/action_delegates/album_set.dart +++ b/lib/widgets/filter_grids/common/action_delegates/album_set.dart @@ -147,25 +147,19 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate with // general case ChipSetAction.createAlbum: _createAlbum(context, locked: false); - break; case ChipSetAction.createVault: _createAlbum(context, locked: true); - break; // single/multiple filters case ChipSetAction.delete: _delete(context, filters); - break; case ChipSetAction.lockVault: lockFilters(filters); browse(context); - break; // single filter case ChipSetAction.rename: _rename(context, filters.first); - break; case ChipSetAction.configureVault: _configureVault(context, filters.first); - break; default: break; } diff --git a/lib/widgets/filter_grids/common/action_delegates/chip.dart b/lib/widgets/filter_grids/common/action_delegates/chip.dart index b5fe5b64a..5d3760a43 100644 --- a/lib/widgets/filter_grids/common/action_delegates/chip.dart +++ b/lib/widgets/filter_grids/common/action_delegates/chip.dart @@ -40,22 +40,16 @@ class ChipActionDelegate with FeedbackMixin, VaultAwareMixin { switch (action) { case ChipAction.goToAlbumPage: _goTo(context, filter, AlbumListPage.routeName, (context) => const AlbumListPage()); - break; case ChipAction.goToCountryPage: _goTo(context, filter, CountryListPage.routeName, (context) => const CountryListPage()); - break; case ChipAction.goToPlacePage: _goTo(context, filter, PlaceListPage.routeName, (context) => const PlaceListPage()); - break; case ChipAction.goToTagPage: _goTo(context, filter, TagListPage.routeName, (context) => const TagListPage()); - break; case ChipAction.reverse: ReverseFilterNotification(filter).dispatch(context); - break; case ChipAction.hide: _hide(context, filter); - break; case ChipAction.lockVault: if (filter is AlbumFilter) { lockFilters({filter}); diff --git a/lib/widgets/filter_grids/common/action_delegates/chip_set.dart b/lib/widgets/filter_grids/common/action_delegates/chip_set.dart index 111dd9960..4f4a8b92d 100644 --- a/lib/widgets/filter_grids/common/action_delegates/chip_set.dart +++ b/lib/widgets/filter_grids/common/action_delegates/chip_set.dart @@ -164,48 +164,36 @@ abstract class ChipSetActionDelegate with FeedbackMi // general case ChipSetAction.configureView: configureView(context); - break; case ChipSetAction.select: context.read>>().select(); - break; case ChipSetAction.selectAll: context.read>>().addToSelection(allItems); - break; case ChipSetAction.selectNone: context.read>>().clearSelection(); - break; // browsing case ChipSetAction.search: _goToSearch(context); - break; case ChipSetAction.toggleTitleSearch: context.read().toggle(); - break; case ChipSetAction.createAlbum: case ChipSetAction.createVault: break; // browsing or selecting case ChipSetAction.map: _goToMap(context, filters); - break; case ChipSetAction.slideshow: _goToSlideshow(context, filters); - break; case ChipSetAction.stats: _goToStats(context, filters); - break; // selecting (single/multiple filters) case ChipSetAction.hide: _hide(context, filters); - break; case ChipSetAction.pin: settings.pinnedFilters = settings.pinnedFilters..addAll(filters); browse(context); - break; case ChipSetAction.unpin: settings.pinnedFilters = settings.pinnedFilters..removeAll(filters); browse(context); - break; case ChipSetAction.delete: case ChipSetAction.lockVault: case ChipSetAction.showCountryStates: @@ -213,7 +201,6 @@ abstract class ChipSetActionDelegate with FeedbackMi // selecting (single filter) case ChipSetAction.setCover: _setCover(context, filters.first); - break; case ChipSetAction.rename: case ChipSetAction.configureVault: break; diff --git a/lib/widgets/filter_grids/common/action_delegates/country_set.dart b/lib/widgets/filter_grids/common/action_delegates/country_set.dart index 0f3cc66dc..669bb67d0 100644 --- a/lib/widgets/filter_grids/common/action_delegates/country_set.dart +++ b/lib/widgets/filter_grids/common/action_delegates/country_set.dart @@ -87,7 +87,6 @@ class CountryChipSetActionDelegate extends ChipSetActionDelegate case ChipSetAction.showCountryStates: _showStates(context, filters); browse(context); - break; default: break; } diff --git a/lib/widgets/filter_grids/common/action_delegates/tag_set.dart b/lib/widgets/filter_grids/common/action_delegates/tag_set.dart index d669b6fab..6c9e2324d 100644 --- a/lib/widgets/filter_grids/common/action_delegates/tag_set.dart +++ b/lib/widgets/filter_grids/common/action_delegates/tag_set.dart @@ -70,7 +70,6 @@ class TagChipSetActionDelegate extends ChipSetActionDelegate { // single/multiple filters case ChipSetAction.delete: _delete(context, filters); - break; default: break; } diff --git a/lib/widgets/filter_grids/common/app_bar.dart b/lib/widgets/filter_grids/common/app_bar.dart index 7c752c228..435576e71 100644 --- a/lib/widgets/filter_grids/common/app_bar.dart +++ b/lib/widgets/filter_grids/common/app_bar.dart @@ -61,10 +61,8 @@ class FilterGridAppBar().enabled, isMenuItem: true, ); - break; default: child = MenuRow(text: action.getText(context), icon: action.getIcon()); - break; } return PopupMenuItem( @@ -180,7 +178,7 @@ class _FilterGridAppBarState().textScaleFactor; + final textScaleFactor = MediaQuery.textScaleFactorOf(context); double height = kToolbarHeight * textScaleFactor; if (settings.useTvLayout) { height += CaptionedButton.getTelevisionButtonHeight(context); diff --git a/lib/widgets/filter_grids/common/covered_filter_chip.dart b/lib/widgets/filter_grids/common/covered_filter_chip.dart index a2e066edf..d1fa7f60c 100644 --- a/lib/widgets/filter_grids/common/covered_filter_chip.dart +++ b/lib/widgets/filter_grids/common/covered_filter_chip.dart @@ -111,26 +111,22 @@ class CoveredFilterChip extends StatelessWidget { // album filters themselves do not change, but decoration derived from it does chipKey = ValueKey(appInventory.areAppNamesReadyNotifier.value); } + final textScaleFactor = MediaQuery.textScaleFactorOf(context); return AvesFilterChip( key: chipKey, filter: _filter, showText: showText, showGenericIcon: false, decoration: AvesFilterDecoration( - widget: Selector( - selector: (context, mq) => mq.textScaleFactor, - builder: (context, textScaleFactor, child) { - return Padding( - padding: EdgeInsets.only( - bottom: infoHeight( - extent: extent, - textScaleFactor: textScaleFactor, - showText: showText, - ), - ), - child: child, - ); - }, + radius: radius(extent), + widget: Padding( + padding: EdgeInsets.only( + bottom: infoHeight( + extent: extent, + textScaleFactor: textScaleFactor, + showText: showText, + ), + ), child: entry == null ? StreamBuilder?>( stream: covers.colorChangeStream.where((event) => event == null || event.contains(_filter)), @@ -159,9 +155,9 @@ class CoveredFilterChip extends StatelessWidget { : ThumbnailImage( entry: entry, extent: thumbnailExtent, + devicePixelRatio: MediaQuery.devicePixelRatioOf(context), ), ), - radius: radius(extent), ), banner: banner, details: showText ? _buildDetails(context, source, _filter) : null, diff --git a/lib/widgets/filter_grids/common/filter_grid_page.dart b/lib/widgets/filter_grids/common/filter_grid_page.dart index 39567170b..7daaca92a 100644 --- a/lib/widgets/filter_grids/common/filter_grid_page.dart +++ b/lib/widgets/filter_grids/common/filter_grid_page.dart @@ -86,28 +86,23 @@ class FilterGridPage extends StatelessWidget { start: !useTvLayout, top: false, bottom: false, - child: Selector( - selector: (context, mq) => mq.padding.top, - builder: (context, mqPaddingTop, child) { - return ValueListenableBuilder( - valueListenable: appBarHeightNotifier, - builder: (context, appBarHeight, child) { - return _FilterGrid( - // key is expected by test driver - key: const Key('filter-grid'), - settingsRouteKey: settingsRouteKey, - appBar: appBar, - appBarHeight: mqPaddingTop + appBarHeight, - sections: sections, - newFilters: newFilters, - sortFactor: sortFactor, - showHeaders: showHeaders, - selectable: selectable, - applyQuery: applyQuery, - emptyBuilder: emptyBuilder, - heroType: heroType, - ); - }, + child: ValueListenableBuilder( + valueListenable: appBarHeightNotifier, + builder: (context, appBarHeight, child) { + return _FilterGrid( + // key is expected by test driver + key: const Key('filter-grid'), + settingsRouteKey: settingsRouteKey, + appBar: appBar, + appBarHeight: MediaQuery.paddingOf(context).top + appBarHeight, + sections: sections, + newFilters: newFilters, + sortFactor: sortFactor, + showHeaders: showHeaders, + selectable: selectable, + applyQuery: applyQuery, + emptyBuilder: emptyBuilder, + heroType: heroType, ); }, ), @@ -332,80 +327,76 @@ class _FilterGridContentState extends State<_FilterG // do not listen for animation delay change final target = context.read().staggeredAnimationPageTarget; final tileAnimationDelay = context.read().getTileAnimationDelay(target); - return Selector( - selector: (context, mq) => mq.textScaleFactor, - builder: (context, textScaleFactor, child) { - final tileHeight = CoveredFilterChip.tileHeight( - extent: thumbnailExtent, - textScaleFactor: textScaleFactor, - showText: tileLayout != TileLayout.list, - ); - return GridTheme( - extent: thumbnailExtent, - child: FilterListDetailsTheme( - extent: thumbnailExtent, - child: AnimatedBuilder( - animation: vaults, - builder: (context, child) { - return SectionedFilterListLayoutProvider( - sections: visibleSections, - showHeaders: widget.showHeaders, - selectable: widget.selectable, - tileLayout: tileLayout, - scrollableWidth: scrollableWidth, - columnCount: columnCount, - spacing: tileSpacing, - horizontalPadding: horizontalPadding, - tileWidth: thumbnailExtent, - tileHeight: tileHeight, - tileBuilder: (gridItem, tileSize) { - final extent = tileSize.shortestSide; - final tile = InteractiveFilterTile( - gridItem: gridItem, - chipExtent: extent, - thumbnailExtent: extent, - tileLayout: tileLayout, - banner: _getFilterBanner(context, gridItem.filter), - heroType: widget.heroType, - ); - if (!settings.useTvLayout) return tile; - return Focus( - onFocusChange: (focused) { - if (focused) { - _focusedItemNotifier.value = gridItem; - } else if (_focusedItemNotifier.value == gridItem) { - _focusedItemNotifier.value = null; - } - }, - child: ValueListenableBuilder?>( - valueListenable: _focusedItemNotifier, - builder: (context, focusedItem, child) { - return AnimatedScale( - scale: focusedItem == gridItem ? 1 : .9, - curve: Curves.fastOutSlowIn, - duration: context.select((v) => v.tvImageFocusAnimation), - child: child!, - ); - }, - child: tile, - ), - ); + final textScaleFactor = MediaQuery.textScaleFactorOf(context); + final tileHeight = CoveredFilterChip.tileHeight( + extent: thumbnailExtent, + textScaleFactor: textScaleFactor, + showText: tileLayout != TileLayout.list, + ); + return GridTheme( + extent: thumbnailExtent, + child: FilterListDetailsTheme( + extent: thumbnailExtent, + child: AnimatedBuilder( + animation: vaults, + builder: (context, child) { + return SectionedFilterListLayoutProvider( + sections: visibleSections, + showHeaders: widget.showHeaders, + selectable: widget.selectable, + tileLayout: tileLayout, + scrollableWidth: scrollableWidth, + columnCount: columnCount, + spacing: tileSpacing, + horizontalPadding: horizontalPadding, + tileWidth: thumbnailExtent, + tileHeight: tileHeight, + tileBuilder: (gridItem, tileSize) { + final extent = tileSize.shortestSide; + final tile = InteractiveFilterTile( + gridItem: gridItem, + chipExtent: extent, + thumbnailExtent: extent, + tileLayout: tileLayout, + banner: _getFilterBanner(context, gridItem.filter), + heroType: widget.heroType, + ); + if (!settings.useTvLayout) return tile; + + return Focus( + onFocusChange: (focused) { + if (focused) { + _focusedItemNotifier.value = gridItem; + } else if (_focusedItemNotifier.value == gridItem) { + _focusedItemNotifier.value = null; + } }, - tileAnimationDelay: tileAnimationDelay, - coverRatioResolver: (item) { - final coverEntry = source.coverEntry(item.filter) ?? item.entry; - return coverEntry?.displayAspectRatio ?? 1; - }, - child: child!, + child: ValueListenableBuilder?>( + valueListenable: _focusedItemNotifier, + builder: (context, focusedItem, child) { + return AnimatedScale( + scale: focusedItem == gridItem ? 1 : .9, + curve: Curves.fastOutSlowIn, + duration: context.select((v) => v.tvImageFocusAnimation), + child: child!, + ); + }, + child: tile, + ), ); }, - child: child, - ), - ), - ); - }, - child: child, + tileAnimationDelay: tileAnimationDelay, + coverRatioResolver: (item) { + final coverEntry = source.coverEntry(item.filter) ?? item.entry; + return coverEntry?.displayAspectRatio ?? 1; + }, + child: child!, + ); + }, + child: child, + ), + ), ); }, child: child, @@ -579,7 +570,7 @@ class _FilterScaler extends StatelessWidget { @override Widget build(BuildContext context) { - final textScaleFactor = context.select((mq) => mq.textScaleFactor); + final textScaleFactor = MediaQuery.textScaleFactorOf(context); final metrics = context.select>((v) => Tuple2(v.spacing, v.horizontalPadding)); final tileSpacing = metrics.item1; final horizontalPadding = metrics.item2; diff --git a/lib/widgets/filter_grids/common/filter_nav_page.dart b/lib/widgets/filter_grids/common/filter_nav_page.dart index b2a156655..864bef00d 100644 --- a/lib/widgets/filter_grids/common/filter_nav_page.dart +++ b/lib/widgets/filter_grids/common/filter_nav_page.dart @@ -76,22 +76,18 @@ class FilterNavigationPage MapEntry(filter, source.count(filter)))); filtersWithCount.sort(compareFiltersByEntryCount); filters = filtersWithCount.map((kv) => kv.key).toSet(); allMapEntries = toGridItem(source, filters); - break; case ChipSortFactor.size: final filtersWithSize = List.of(filters.map((filter) => MapEntry(filter, source.size(filter)))); filtersWithSize.sort(compareFiltersBySize); filters = filtersWithSize.map((kv) => kv.key).toSet(); allMapEntries = toGridItem(source, filters); - break; } if (reverse) { allMapEntries = allMapEntries.reversed.toList(); diff --git a/lib/widgets/filter_grids/common/filter_tile.dart b/lib/widgets/filter_grids/common/filter_tile.dart index 4ac473b8c..b9b1b64f4 100644 --- a/lib/widgets/filter_grids/common/filter_tile.dart +++ b/lib/widgets/filter_grids/common/filter_tile.dart @@ -65,10 +65,8 @@ class _InteractiveFilterTileState extends State(filter); - break; case AppMode.pickMediaInternal: case AppMode.screenSaver: case AppMode.setWallpaper: diff --git a/lib/widgets/filter_grids/common/list_details.dart b/lib/widgets/filter_grids/common/list_details.dart index 37e6224c4..872fef703 100644 --- a/lib/widgets/filter_grids/common/list_details.dart +++ b/lib/widgets/filter_grids/common/list_details.dart @@ -86,7 +86,7 @@ class FilterListDetails extends StatelessWidget { Widget _buildDateRow(BuildContext context, FilterListDetailsThemeData detailsTheme, bool hasTitleLeading) { final locale = context.l10n.localeName; - final use24hour = context.select((v) => v.alwaysUse24HourFormat); + final use24hour = MediaQuery.alwaysUse24HourFormatOf(context); final date = entry?.bestDate; final dateText = date != null ? formatDateTime(date, locale, use24hour) : AText.valueNotAvailable; diff --git a/lib/widgets/filter_grids/common/query_bar.dart b/lib/widgets/filter_grids/common/query_bar.dart index 4f861b861..a6a09228b 100644 --- a/lib/widgets/filter_grids/common/query_bar.dart +++ b/lib/widgets/filter_grids/common/query_bar.dart @@ -17,7 +17,7 @@ class FilterQueryBar extends StatelessWidget { @override Widget build(BuildContext context) { - final textScaleFactor = context.select((mq) => mq.textScaleFactor); + final textScaleFactor = MediaQuery.textScaleFactorOf(context); return Container( height: getPreferredHeight(textScaleFactor), alignment: Alignment.topCenter, diff --git a/lib/widgets/home_page.dart b/lib/widgets/home_page.dart index f2ea00c12..fd3258664 100644 --- a/lib/widgets/home_page.dart +++ b/lib/widgets/home_page.dart @@ -129,10 +129,8 @@ class _HomePageState extends State { break; case WidgetOpenPage.collection: _initialFilters = settings.getWidgetCollectionFilters(widgetId); - break; case WidgetOpenPage.viewer: uri = settings.getWidgetUri(widgetId); - break; } unawaited(WidgetService.update(widgetId)); } else { @@ -148,7 +146,6 @@ class _HomePageState extends State { appMode = AppMode.view; } } - break; case actionPickItems: // TODO TLAD apply pick mimetype(s) // some apps define multiple types, separated by a space (maybe other signs too, like `,` `;`?) @@ -156,32 +153,25 @@ class _HomePageState extends State { final multiple = intentData[intentDataKeyAllowMultiple] ?? false; debugPrint('pick mimeType=$pickMimeTypes multiple=$multiple'); appMode = multiple ? AppMode.pickMultipleMediaExternal : AppMode.pickSingleMediaExternal; - break; case actionPickCollectionFilters: appMode = AppMode.pickCollectionFiltersExternal; - break; case actionScreenSaver: appMode = AppMode.screenSaver; _initialRouteName = ScreenSaverPage.routeName; - break; case actionScreenSaverSettings: _initialRouteName = ScreenSaverSettingsPage.routeName; - break; case actionSearch: _initialRouteName = SearchPage.routeName; _initialSearchQuery = intentData[intentDataKeyQuery]; - break; case actionSetWallpaper: appMode = AppMode.setWallpaper; _viewerEntry = await _initViewerEntry( uri: intentData[intentDataKeyUri], mimeType: intentData[intentDataKeyMimeType], ); - break; case actionWidgetSettings: _initialRouteName = HomeWidgetSettingsPage.routeName; _widgetId = intentData[intentDataKeyWidgetId] ?? 0; - break; default: // do not use 'route' as extra key, as the Flutter framework acts on it final extraRoute = intentData[intentDataKeyPage]; @@ -210,13 +200,11 @@ class _HomePageState extends State { loadTopEntriesFirst: settings.homePage == HomePageSetting.collection, canAnalyze: !safeMode, ); - break; case AppMode.screenSaver: final source = context.read(); await source.init( canAnalyze: false, ); - break; case AppMode.view: if (_isViewerSourceable(_viewerEntry)) { final directory = _viewerEntry?.directory; @@ -231,10 +219,8 @@ class _HomePageState extends State { } else { await _initViewerEssentials(); } - break; case AppMode.setWallpaper: await _initViewerEssentials(); - break; case AppMode.pickMediaInternal: case AppMode.pickFilterInternal: case AppMode.slideshow: @@ -340,11 +326,9 @@ class _HomePageState extends State { case AppMode.pickSingleMediaExternal: case AppMode.pickMultipleMediaExternal: routeName = CollectionPage.routeName; - break; default: routeName = _initialRouteName ?? settings.homePage.routeName; filters = _initialFilters ?? {}; - break; } Route buildRoute(WidgetBuilder builder) => DirectMaterialPageRoute( settings: RouteSettings(name: routeName), diff --git a/lib/widgets/map/address_row.dart b/lib/widgets/map/address_row.dart index 407e24aab..daeab20ed 100644 --- a/lib/widgets/map/address_row.dart +++ b/lib/widgets/map/address_row.dart @@ -9,7 +9,6 @@ import 'package:aves/theme/styles.dart'; import 'package:aves/theme/text.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; import 'info_row.dart'; @@ -44,12 +43,13 @@ class _MapAddressRowState extends State { @override Widget build(BuildContext context) { + final textScaleFactor = MediaQuery.textScaleFactorOf(context); return Container( alignment: AlignmentDirectional.centerStart, // addresses can include non-latin scripts with inconsistent line height, // which is especially an issue for relayout/painting of heavy Google map, // so we give extra height to give breathing room to the text and stabilize layout - height: Theme.of(context).textTheme.bodyMedium!.fontSize! * context.select((mq) => mq.textScaleFactor) * 2, + height: Theme.of(context).textTheme.bodyMedium!.fontSize! * textScaleFactor * 2, child: ValueListenableBuilder( valueListenable: _addressLineNotifier, builder: (context, addressLine, child) { diff --git a/lib/widgets/map/date_row.dart b/lib/widgets/map/date_row.dart index 430a731fc..33e5893ca 100644 --- a/lib/widgets/map/date_row.dart +++ b/lib/widgets/map/date_row.dart @@ -6,7 +6,6 @@ import 'package:aves/theme/text.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/map/info_row.dart'; import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; class MapDateRow extends StatelessWidget { final AvesEntry? entry; @@ -19,7 +18,7 @@ class MapDateRow extends StatelessWidget { @override Widget build(BuildContext context) { final locale = context.l10n.localeName; - final use24hour = context.select((v) => v.alwaysUse24HourFormat); + final use24hour = MediaQuery.alwaysUse24HourFormatOf(context); final date = entry?.bestDate; final dateText = date != null ? formatDateTime(date, locale, use24hour) : AText.valueNotAvailable; diff --git a/lib/widgets/map/info_row.dart b/lib/widgets/map/info_row.dart index fc58e1387..2bbb4eb25 100644 --- a/lib/widgets/map/info_row.dart +++ b/lib/widgets/map/info_row.dart @@ -3,7 +3,6 @@ import 'package:aves/widgets/map/address_row.dart'; import 'package:aves/widgets/map/date_row.dart'; import 'package:aves_map/aves_map.dart'; import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; class MapInfoRow extends StatelessWidget { final ValueNotifier entryNotifier; @@ -18,12 +17,11 @@ class MapInfoRow extends StatelessWidget { @override Widget build(BuildContext context) { - final orientation = context.select((v) => v.orientation); - return ValueListenableBuilder( valueListenable: entryNotifier, builder: (context, entry, child) { - final content = orientation == Orientation.portrait + final isPortrait = MediaQuery.orientationOf(context) == Orientation.portrait; + final content = isPortrait ? [ Expanded( child: Column( @@ -59,5 +57,8 @@ class MapInfoRow extends StatelessWidget { ); } - static double getIconSize(BuildContext context) => 16.0 * context.select((mq) => mq.textScaleFactor); + static double getIconSize(BuildContext context) { + final textScaleFactor = MediaQuery.textScaleFactorOf(context); + return 16 * textScaleFactor; + } } diff --git a/lib/widgets/map/map_page.dart b/lib/widgets/map/map_page.dart index 63a463d09..665ccc8ca 100644 --- a/lib/widgets/map/map_page.dart +++ b/lib/widgets/map/map_page.dart @@ -247,6 +247,7 @@ class _ContentState extends State<_Content> with SingleTickerProviderStateMixin controller: _mapController, collectionListenable: openingCollection, entries: openingCollection.sortedEntries, + availableSize: MediaQuery.sizeOf(context), initialCenter: widget.initialEntry?.latLng ?? widget.overlayEntry?.center, isAnimatingNotifier: _isPageAnimatingNotifier, dotLocationNotifier: _dotLocationNotifier, @@ -487,10 +488,8 @@ class _ContentState extends State<_Content> with SingleTickerProviderStateMixin } _mapController.moveTo(location); } - break; case MapClusterAction.removeLocation: await delegate.removeLocation(context, clusterEntries); - break; } } } diff --git a/lib/widgets/map/scroller.dart b/lib/widgets/map/scroller.dart index 54c9a1c6b..467503432 100644 --- a/lib/widgets/map/scroller.dart +++ b/lib/widgets/map/scroller.dart @@ -7,7 +7,6 @@ import 'package:aves/widgets/common/identity/empty.dart'; import 'package:aves/widgets/common/thumbnail/scroller.dart'; import 'package:aves/widgets/map/info_row.dart'; import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; class MapEntryScroller extends StatefulWidget { final ValueNotifier regionCollectionNotifier; @@ -71,30 +70,27 @@ class _MapEntryScrollerState extends State { child: MapInfoRow(entryNotifier: _infoEntryNotifier), ), const SizedBox(height: 8), - Selector( - selector: (context, mq) => mq.size.width, - builder: (context, mqWidth, child) => ValueListenableBuilder( - valueListenable: widget.regionCollectionNotifier, - builder: (context, regionCollection, child) { - return AnimatedBuilder( - // update when entries are added/removed - animation: regionCollection ?? ChangeNotifier(), - builder: (context, child) { - final regionEntries = regionCollection?.sortedEntries ?? []; - return ThumbnailScroller( - availableWidth: mqWidth, - entryCount: regionEntries.length, - entryBuilder: (index) => index < regionEntries.length ? regionEntries[index] : null, - indexNotifier: widget.selectedIndexNotifier, - onTap: widget.onTap, - heroTagger: (entry) => Object.hashAll([regionCollection?.id, entry.id]), - highlightable: true, - showLocation: false, - ); - }, - ); - }, - ), + ValueListenableBuilder( + valueListenable: widget.regionCollectionNotifier, + builder: (context, regionCollection, child) { + return AnimatedBuilder( + // update when entries are added/removed + animation: regionCollection ?? ChangeNotifier(), + builder: (context, child) { + final regionEntries = regionCollection?.sortedEntries ?? []; + return ThumbnailScroller( + availableWidth: MediaQuery.sizeOf(context).width, + entryCount: regionEntries.length, + entryBuilder: (index) => index < regionEntries.length ? regionEntries[index] : null, + indexNotifier: widget.selectedIndexNotifier, + onTap: widget.onTap, + heroTagger: (entry) => Object.hashAll([regionCollection?.id, entry.id]), + highlightable: true, + showLocation: false, + ); + }, + ); + }, ), ], ), diff --git a/lib/widgets/navigation/drawer/app_drawer.dart b/lib/widgets/navigation/drawer/app_drawer.dart index 8bb449524..37ac737cf 100644 --- a/lib/widgets/navigation/drawer/app_drawer.dart +++ b/lib/widgets/navigation/drawer/app_drawer.dart @@ -85,6 +85,7 @@ class _AppDrawerState extends State { child: Selector( selector: (context, mq) => mq.effectiveBottomPadding, builder: (context, mqPaddingBottom, child) { + final textScaleFactor = MediaQuery.textScaleFactorOf(context); final iconTheme = IconTheme.of(context); return SingleChildScrollView( controller: _scrollController, @@ -93,7 +94,7 @@ class _AppDrawerState extends State { padding: EdgeInsets.only(bottom: mqPaddingBottom), child: IconTheme( data: iconTheme.copyWith( - size: iconTheme.size! * MediaQuery.textScaleFactorOf(context), + size: iconTheme.size! * textScaleFactor, ), child: Column( children: drawerItems, @@ -238,25 +239,21 @@ class _AppDrawerState extends State { stream: source.eventBus.on(), builder: (context, _) => Text('${source.rawAlbums.length}'), ); - break; case CountryListPage.routeName: trailing = StreamBuilder( stream: source.eventBus.on(), builder: (context, _) => Text('${source.sortedCountries.length}'), ); - break; case PlaceListPage.routeName: trailing = StreamBuilder( stream: source.eventBus.on(), builder: (context, _) => Text('${source.sortedPlaces.length}'), ); - break; case TagListPage.routeName: trailing = StreamBuilder( stream: source.eventBus.on(), builder: (context, _) => Text('${source.sortedTags.length}'), ); - break; } return PageNavTile( diff --git a/lib/widgets/navigation/nav_bar/floating.dart b/lib/widgets/navigation/nav_bar/floating.dart index 6ebb4bfdd..85d729eb3 100644 --- a/lib/widgets/navigation/nav_bar/floating.dart +++ b/lib/widgets/navigation/nav_bar/floating.dart @@ -113,10 +113,8 @@ class _FloatingNavBarState extends State with SingleTickerProvid switch (event) { case DraggableScrollbarEvent.dragStart: _isDragging = true; - break; case DraggableScrollbarEvent.dragEnd: _isDragging = false; - break; } } } diff --git a/lib/widgets/settings/app_export/items.dart b/lib/widgets/settings/app_export/items.dart index 3a94614b4..5ee21faa5 100644 --- a/lib/widgets/settings/app_export/items.dart +++ b/lib/widgets/settings/app_export/items.dart @@ -34,13 +34,10 @@ extension ExtraAppExportItem on AppExportItem { switch (this) { case AppExportItem.covers: covers.import(jsonMap, source); - break; case AppExportItem.favourites: favourites.import(jsonMap, source); - break; case AppExportItem.settings: await settings.import(jsonMap); - break; } } } diff --git a/lib/widgets/settings/common/quick_actions/editor_page.dart b/lib/widgets/settings/common/quick_actions/editor_page.dart index 0bec2c037..4ba51136c 100644 --- a/lib/widgets/settings/common/quick_actions/editor_page.dart +++ b/lib/widgets/settings/common/quick_actions/editor_page.dart @@ -317,13 +317,10 @@ class _QuickActionEditorBodyState extends State { case _PickerAction.toggleHiddenView: settings.filePickerShowHiddenFiles = !showHidden; setState(() {}); - break; } }, ), diff --git a/lib/widgets/settings/settings_mobile_page.dart b/lib/widgets/settings/settings_mobile_page.dart index 2950b0148..354e613f0 100644 --- a/lib/widgets/settings/settings_mobile_page.dart +++ b/lib/widgets/settings/settings_mobile_page.dart @@ -124,7 +124,6 @@ class _SettingsMobilePageState extends State with FeedbackMi showFeedback(context, context.l10n.genericFailureFeedback); } } - break; case SettingsAction.import: // specifying the JSON MIME type to restrict openable files is correct in theory, // but older devices (e.g. SM-P580, API 27) that do not recognize JSON files as such would filter them out @@ -172,7 +171,6 @@ class _SettingsMobilePageState extends State with FeedbackMi showFeedback(context, context.l10n.genericFailureFeedback); } } - break; } } diff --git a/lib/widgets/settings/thumbnails/overlay.dart b/lib/widgets/settings/thumbnails/overlay.dart index 3f8be2e1d..d136ddbe1 100644 --- a/lib/widgets/settings/thumbnails/overlay.dart +++ b/lib/widgets/settings/thumbnails/overlay.dart @@ -89,7 +89,10 @@ class ThumbnailOverlayPage extends StatelessWidget { ); } - static double _getIconSize(BuildContext context) => IconTheme.of(context).size! * MediaQuery.textScaleFactorOf(context); + static double _getIconSize(BuildContext context) { + final textScaleFactor = MediaQuery.textScaleFactorOf(context); + return IconTheme.of(context).size! * textScaleFactor; + } static Color _getIconColor(BuildContext context) => context.select((v) => v.neutral); diff --git a/lib/widgets/stats/date/histogram.dart b/lib/widgets/stats/date/histogram.dart index 772c07310..69acdcc41 100644 --- a/lib/widgets/stats/date/histogram.dart +++ b/lib/widgets/stats/date/histogram.dart @@ -68,13 +68,10 @@ class _HistogramState extends State with AutomaticKeepAliveClientMixi switch (_level) { case DateLevel.ymd: normalizeDate = (v) => DateTime(v.year, v.month, v.day); - break; case DateLevel.ym: normalizeDate = (v) => DateTime(v.year, v.month); - break; default: normalizeDate = (v) => DateTime(v.year); - break; } _firstDate = normalizeDate(firstDate); _lastDate = normalizeDate(lastDate); @@ -118,15 +115,12 @@ class _HistogramState extends State with AutomaticKeepAliveClientMixi case DateLevel.ymd: xCount = xRange.inDays; incrementDate = (date) => DateTime(date.year, date.month, date.day + 1); - break; case DateLevel.ym: xCount = (xRange.inDays / 30.5).round(); incrementDate = (date) => DateTime(date.year, date.month + 1); - break; default: xCount = lastDate.year - firstDate.year; incrementDate = (date) => DateTime(date.year + 1); - break; } final yMax = entryCountPerDate.values.reduce(max).toDouble(); final xInterval = yMax / xCount; diff --git a/lib/widgets/viewer/action/entry_action_delegate.dart b/lib/widgets/viewer/action/entry_action_delegate.dart index d4e099a93..d31760d64 100644 --- a/lib/widgets/viewer/action/entry_action_delegate.dart +++ b/lib/widgets/viewer/action/entry_action_delegate.dart @@ -181,61 +181,44 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix switch (action) { case EntryAction.info: ShowInfoPageNotification().dispatch(context); - break; case EntryAction.addShortcut: _addShortcut(context, targetEntry); - break; case EntryAction.copyToClipboard: appService.copyToClipboard(targetEntry.uri, targetEntry.bestTitle).then((success) { showFeedback(context, success ? context.l10n.genericSuccessFeedback : context.l10n.genericFailureFeedback); }); - break; case EntryAction.delete: _delete(context, targetEntry); - break; case EntryAction.restore: _move(context, targetEntry, moveType: MoveType.fromBin); - break; case EntryAction.convert: convert(context, {targetEntry}); - break; case EntryAction.print: EntryPrinter(targetEntry).print(context); - break; case EntryAction.rename: _rename(context, targetEntry); - break; case EntryAction.copy: _move(context, targetEntry, moveType: MoveType.copy); - break; case EntryAction.move: _move(context, targetEntry, moveType: MoveType.move); - break; case EntryAction.share: appService.shareEntries({targetEntry}).then((success) { if (!success) showNoMatchingAppDialog(context); }); - break; case EntryAction.toggleFavourite: targetEntry.toggleFavourite(); - break; // raster case EntryAction.rotateCCW: _rotate(context, targetEntry, clockwise: false); - break; case EntryAction.rotateCW: _rotate(context, targetEntry, clockwise: true); - break; case EntryAction.flip: _flip(context, targetEntry); - break; // vector case EntryAction.viewSource: _goToSourceViewer(context, targetEntry); - break; case EntryAction.lockViewer: const LockViewNotification(locked: true).dispatch(context); - break; // video case EntryAction.videoCaptureFrame: case EntryAction.videoToggleMute: @@ -254,31 +237,25 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix action: action, ).dispatch(context); } - break; case EntryAction.edit: appService.edit(targetEntry.uri, targetEntry.mimeType).then((success) { if (!success) showNoMatchingAppDialog(context); }); - break; case EntryAction.open: appService.open(targetEntry.uri, targetEntry.mimeTypeAnySubtype, forceChooser: true).then((success) { if (!success) showNoMatchingAppDialog(context); }); - break; case EntryAction.openMap: appService.openMap(targetEntry.latLng!).then((success) { if (!success) showNoMatchingAppDialog(context); }); - break; case EntryAction.setAs: appService.setAs(targetEntry.uri, targetEntry.mimeType).then((success) { if (!success) showNoMatchingAppDialog(context); }); - break; // platform case EntryAction.rotateScreen: _rotateScreen(context); - break; // metadata case EntryAction.editDate: case EntryAction.editLocation: @@ -291,11 +268,9 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix case EntryAction.convertMotionPhotoToStillImage: case EntryAction.viewMotionPhotoVideo: _metadataActionDelegate.onActionSelected(context, targetEntry, collection, action); - break; // debug case EntryAction.debug: _goToDebug(context, targetEntry); - break; } } @@ -321,13 +296,11 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix final fields = await embeddedDataService.extractMotionPhotoImage(mainEntry); await _shareMotionPhotoPart(context, fields); } - break; case ShareAction.videoOnly: if (mainEntry.isMotionPhoto) { final fields = await embeddedDataService.extractMotionPhotoVideo(mainEntry); await _shareMotionPhotoPart(context, fields); } - break; } } @@ -379,14 +352,8 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix } Future _rotateScreen(BuildContext context) async { - switch (context.read().orientation) { - case Orientation.landscape: - await windowService.requestOrientation(Orientation.portrait); - break; - case Orientation.portrait: - await windowService.requestOrientation(Orientation.landscape); - break; - } + final isPortrait = MediaQuery.orientationOf(context) == Orientation.portrait; + await windowService.requestOrientation(isPortrait ? Orientation.landscape : Orientation.portrait); } Future _delete(BuildContext context, AvesEntry targetEntry) async { diff --git a/lib/widgets/viewer/action/entry_info_action_delegate.dart b/lib/widgets/viewer/action/entry_info_action_delegate.dart index 33bb488e0..0487d219e 100644 --- a/lib/widgets/viewer/action/entry_info_action_delegate.dart +++ b/lib/widgets/viewer/action/entry_info_action_delegate.dart @@ -99,40 +99,29 @@ class EntryInfoActionDelegate with FeedbackMixin, PermissionAwareMixin, EntryEdi // general case EntryAction.editDate: await _editDate(context, targetEntry, collection); - break; case EntryAction.editLocation: await _editLocation(context, targetEntry, collection); - break; case EntryAction.editTitleDescription: await _editTitleDescription(context, targetEntry); - break; case EntryAction.editRating: await _editRating(context, targetEntry); - break; case EntryAction.editTags: await _editTags(context, targetEntry); - break; case EntryAction.removeMetadata: await _removeMetadata(context, targetEntry); - break; case EntryAction.exportMetadata: await _exportMetadata(context, targetEntry); - break; // GeoTIFF case EntryAction.showGeoTiffOnMap: await _showGeoTiffOnMap(context, targetEntry, collection); - break; // motion photo case EntryAction.convertMotionPhotoToStillImage: await _convertMotionPhotoToStillImage(context, targetEntry); - break; case EntryAction.viewMotionPhotoVideo: OpenEmbeddedDataNotification.motionPhotoVideo().dispatch(context); - break; // debug case EntryAction.debug: _goToDebug(context, targetEntry); - break; default: break; } @@ -262,6 +251,7 @@ class EntryInfoActionDelegate with FeedbackMixin, PermissionAwareMixin, EntryEdi final mappedGeoTiff = MappedGeoTiff( info: info, entry: targetEntry, + devicePixelRatio: View.of(context).devicePixelRatio, ); if (!mappedGeoTiff.canOverlay) return; diff --git a/lib/widgets/viewer/action/video_action_delegate.dart b/lib/widgets/viewer/action/video_action_delegate.dart index bff1f5412..e9a9d454f 100644 --- a/lib/widgets/viewer/action/video_action_delegate.dart +++ b/lib/widgets/viewer/action/video_action_delegate.dart @@ -45,33 +45,24 @@ class VideoActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix switch (action) { case EntryAction.videoCaptureFrame: await _captureFrame(context, entry, controller); - break; case EntryAction.videoToggleMute: await controller.mute(!controller.isMuted); - break; case EntryAction.videoSelectStreams: await _showStreamSelectionDialog(context, controller); - break; case EntryAction.videoSetSpeed: await _showSpeedDialog(context, controller); - break; case EntryAction.videoSettings: await _showSettings(context, controller); - break; case EntryAction.videoTogglePlay: await _togglePlayPause(context, controller); - break; case EntryAction.videoReplay10: await controller.seekTo(controller.currentPosition - 10000); - break; case EntryAction.videoSkip10: await controller.seekTo(controller.currentPosition + 10000); - break; case EntryAction.openVideo: await appService.open(entry.uri, entry.mimeTypeAnySubtype, forceChooser: false).then((success) { if (!success) showNoMatchingAppDialog(context); }); - break; default: throw UnsupportedError('$action is not a video action'); } diff --git a/lib/widgets/viewer/entry_horizontal_pager.dart b/lib/widgets/viewer/entry_horizontal_pager.dart index c491edad4..c6a11a9f2 100644 --- a/lib/widgets/viewer/entry_horizontal_pager.dart +++ b/lib/widgets/viewer/entry_horizontal_pager.dart @@ -12,7 +12,6 @@ import 'package:aves/widgets/viewer/page_entry_builder.dart'; import 'package:aves/widgets/viewer/visual/entry_page_view.dart'; import 'package:aves_magnifier/aves_magnifier.dart'; import 'package:collection/collection.dart'; -import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -57,7 +56,7 @@ class _MultiEntryScrollerState extends State with AutomaticK scrollDirection: Axis.horizontal, controller: pageController, physics: MagnifierScrollerPhysics( - gestureSettings: context.select((mq) => mq.gestureSettings), + gestureSettings: MediaQuery.gestureSettingsOf(context), parent: const BouncingScrollPhysics(), ), onPageChanged: widget.onPageChanged, diff --git a/lib/widgets/viewer/entry_vertical_pager.dart b/lib/widgets/viewer/entry_vertical_pager.dart index f48286da9..7e6cd8129 100644 --- a/lib/widgets/viewer/entry_vertical_pager.dart +++ b/lib/widgets/viewer/entry_vertical_pager.dart @@ -22,7 +22,6 @@ import 'package:aves/widgets/viewer/multipage/conductor.dart'; import 'package:aves/widgets/viewer/video/conductor.dart'; import 'package:aves_magnifier/aves_magnifier.dart'; import 'package:aves_model/aves_model.dart'; -import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; @@ -187,7 +186,7 @@ class _ViewerVerticalPageViewState extends State { scrollDirection: Axis.vertical, controller: widget.verticalPager, physics: MagnifierScrollerPhysics( - gestureSettings: context.select((mq) => mq.gestureSettings), + gestureSettings: MediaQuery.gestureSettingsOf(context), parent: SpringyScrollPhysics( spring: ViewerVerticalPageView.spring, ), @@ -384,13 +383,10 @@ class _ViewerVerticalPageViewState extends State { switch (intent.type) { case TvPlayPauseType.play: toggle = !controller.isPlaying; - break; case TvPlayPauseType.pause: toggle = controller.isPlaying; - break; case TvPlayPauseType.toggle: toggle = true; - break; } if (toggle) { VideoActionNotification( diff --git a/lib/widgets/viewer/entry_viewer_stack.dart b/lib/widgets/viewer/entry_viewer_stack.dart index f5c601b0d..03093cb45 100644 --- a/lib/widgets/viewer/entry_viewer_stack.dart +++ b/lib/widgets/viewer/entry_viewer_stack.dart @@ -286,14 +286,11 @@ class _EntryViewerStackState extends State with EntryViewContr switch (AvesApp.lifecycleStateNotifier.value) { case AppLifecycleState.inactive: _onAppInactive(); - break; case AppLifecycleState.paused: case AppLifecycleState.detached: pauseVideoControllers(); - break; case AppLifecycleState.resumed: availability.onResume(); - break; } } @@ -525,19 +522,16 @@ class _EntryViewerStackState extends State with EntryViewContr switch (notification.moveType) { case MoveType.move: _onEntryRemoved(context, entries); - break; case MoveType.toBin: if (!isBin) { _onEntryRemoved(context, entries); } - break; case MoveType.fromBin: if (isBin) { _onEntryRemoved(context, entries); } else { _onEntryRestored(entries); } - break; case MoveType.copy: case MoveType.export: break; @@ -639,15 +633,12 @@ class _EntryViewerStackState extends State with EntryViewContr case transitionPage: dismissFeedback(context); _popVisual(); - break; case imagePage: reportService.log('Nav move to Image page'); - break; case infoPage: reportService.log('Nav move to Info page'); // prevent hero when viewer is offscreen _heroInfoNotifier.value = null; - break; } } @@ -821,10 +812,8 @@ class _EntryViewerStackState extends State with EntryViewContr case MaxBrightness.never: case MaxBrightness.viewerOnly: await ScreenBrightness().resetScreenBrightness(); - break; case MaxBrightness.always: await ScreenBrightness().setScreenBrightness(1); - break; } if (settings.keepScreenOn == KeepScreenOn.viewerOnly) { await windowService.keepScreenOn(false); @@ -843,8 +832,7 @@ class _EntryViewerStackState extends State with EntryViewContr final entrySize = videoController.entry.displaySize; final aspectRatio = Rational(entrySize.width.round(), entrySize.height.round()); - final mq = context.read(); - final viewSize = mq.size * mq.devicePixelRatio; + final viewSize = MediaQuery.sizeOf(context) * MediaQuery.devicePixelRatioOf(context); final fittedSize = applyBoxFit(BoxFit.contain, entrySize, viewSize).destination; final sourceRectHint = Rectangle( ((viewSize.width - fittedSize.width) / 2).round(), diff --git a/lib/widgets/viewer/info/basic_section.dart b/lib/widgets/viewer/info/basic_section.dart index db32c6994..ec7de8dde 100644 --- a/lib/widgets/viewer/info/basic_section.dart +++ b/lib/widgets/viewer/info/basic_section.dart @@ -202,21 +202,18 @@ class _BasicSectionState extends State { onChooserValue: (rating) => actionDelegate.quickRate(context, entry, rating), onPressed: onPressed, ); - break; case EntryAction.editTags: button = TagButton( blurred: false, onChooserValue: (filter) => actionDelegate.quickTag(context, entry, filter), onPressed: onPressed, ); - break; default: button = IconButton( icon: action.getIcon(), onPressed: onPressed, tooltip: action.getText(context), ); - break; } return Stack( children: [ @@ -306,7 +303,7 @@ class _BasicInfoState extends State<_BasicInfo> { final l10n = context.l10n; final infoUnknown = l10n.viewerInfoUnknown; final locale = l10n.localeName; - final use24hour = context.select((v) => v.alwaysUse24HourFormat); + final use24hour = MediaQuery.alwaysUse24HourFormatOf(context); // TODO TLAD line break on all characters for the following fields when this is fixed: https://github.com/flutter/flutter/issues/61081 // inserting ZWSP (\u200B) between characters does help, but it messes with width and height computation (another Flutter issue) diff --git a/lib/widgets/viewer/info/embedded/embedded_data_opener.dart b/lib/widgets/viewer/info/embedded/embedded_data_opener.dart index 287a712fb..54faa81f6 100644 --- a/lib/widgets/viewer/info/embedded/embedded_data_opener.dart +++ b/lib/widgets/viewer/info/embedded/embedded_data_opener.dart @@ -42,16 +42,12 @@ class EmbeddedDataOpener extends StatelessWidget with FeedbackMixin { switch (notification.source) { case EmbeddedDataSource.googleDevice: fields = await embeddedDataService.extractGoogleDeviceItem(entry, notification.dataUri!); - break; case EmbeddedDataSource.motionPhotoVideo: fields = await embeddedDataService.extractMotionPhotoVideo(entry); - break; case EmbeddedDataSource.videoCover: fields = await embeddedDataService.extractVideoEmbeddedPicture(entry); - break; case EmbeddedDataSource.xmp: fields = await embeddedDataService.extractXmpDataProp(entry, notification.props, notification.mimeType); - break; } if (!fields.containsKey('mimeType') || !fields.containsKey('uri')) { showFeedback(context, context.l10n.viewerInfoOpenEmbeddedFailureFeedback); diff --git a/lib/widgets/viewer/info/info_page.dart b/lib/widgets/viewer/info/info_page.dart index 440427f9f..1529e11bb 100644 --- a/lib/widgets/viewer/info/info_page.dart +++ b/lib/widgets/viewer/info/info_page.dart @@ -61,39 +61,34 @@ class _InfoPageState extends State { bottom: false, child: NotificationListener( onNotification: _handleTopScroll, - child: Selector( - selector: (context, mq) => mq.size.width, - builder: (context, mqWidth, child) { - return ValueListenableBuilder( - valueListenable: widget.entryNotifier, - builder: (context, mainEntry, child) { - if (mainEntry == null) return const SizedBox(); + child: ValueListenableBuilder( + valueListenable: widget.entryNotifier, + builder: (context, mainEntry, child) { + if (mainEntry == null) return const SizedBox(); - final isSelecting = context.select?, bool>((v) => v?.isSelecting ?? false); - Widget _buildContent({AvesEntry? pageEntry}) { - final targetEntry = pageEntry ?? mainEntry; - return EmbeddedDataOpener( - enabled: !isSelecting, - entry: targetEntry, - child: _InfoPageContent( - collection: widget.collection, - entry: targetEntry, - isScrollingNotifier: widget.isScrollingNotifier, - scrollController: _scrollController, - split: mqWidth > splitScreenWidthThreshold, - goToViewer: _goToViewer, - ), - ); - } + final isSelecting = context.select?, bool>((v) => v?.isSelecting ?? false); + Widget _buildContent({AvesEntry? pageEntry}) { + final targetEntry = pageEntry ?? mainEntry; + return EmbeddedDataOpener( + enabled: !isSelecting, + entry: targetEntry, + child: _InfoPageContent( + collection: widget.collection, + entry: targetEntry, + isScrollingNotifier: widget.isScrollingNotifier, + scrollController: _scrollController, + split: MediaQuery.sizeOf(context).width > splitScreenWidthThreshold, + goToViewer: _goToViewer, + ), + ); + } - return mainEntry.isBurst - ? PageEntryBuilder( - multiPageController: context.read().getController(mainEntry), - builder: (pageEntry) => _buildContent(pageEntry: pageEntry), - ) - : _buildContent(); - }, - ); + return mainEntry.isBurst + ? PageEntryBuilder( + multiPageController: context.read().getController(mainEntry), + builder: (pageEntry) => _buildContent(pageEntry: pageEntry), + ) + : _buildContent(); }, ), ), diff --git a/lib/widgets/viewer/info/location_section.dart b/lib/widgets/viewer/info/location_section.dart index 6e5abc3f4..1010fc999 100644 --- a/lib/widgets/viewer/info/location_section.dart +++ b/lib/widgets/viewer/info/location_section.dart @@ -89,6 +89,7 @@ class _LocationSectionState extends State { child: GeoMap( controller: _mapController, entries: [entry], + availableSize: MediaQuery.sizeOf(context), isAnimatingNotifier: widget.isScrollingNotifier, onUserZoomChange: (zoom) => settings.infoMapZoom = zoom.roundToDouble(), onMarkerTap: collection != null && canNavigate ? (location, entry) => _openMapPage(context) : null, diff --git a/lib/widgets/viewer/info/metadata/metadata_dir_tile.dart b/lib/widgets/viewer/info/metadata/metadata_dir_tile.dart index b999e44ce..0fa09df08 100644 --- a/lib/widgets/viewer/info/metadata/metadata_dir_tile.dart +++ b/lib/widgets/viewer/info/metadata/metadata_dir_tile.dart @@ -98,16 +98,13 @@ class MetadataDirTileBody extends StatelessWidget { switch (dirName) { case SvgMetadataService.metadataDirectory: linkHandlers = getSvgLinkHandlers(tags); - break; case MetadataDirectory.coverDirectory: linkHandlers = getVideoCoverLinkHandlers(tags); - break; case MetadataDirectory.geoTiffDirectory: tags = SplayTreeMap.from(tags.map((name, value) { final tag = GeoTiffDirectory.tagForName(name); return MapEntry(name, GeoTiffDirectory.formatValue(tag, value)); })); - break; } children = [ diff --git a/lib/widgets/viewer/info/metadata/metadata_thumbnail.dart b/lib/widgets/viewer/info/metadata/metadata_thumbnail.dart index 77d0407fc..f4d3f2ba5 100644 --- a/lib/widgets/viewer/info/metadata/metadata_thumbnail.dart +++ b/lib/widgets/viewer/info/metadata/metadata_thumbnail.dart @@ -4,7 +4,6 @@ import 'dart:typed_data'; import 'package:aves/model/entry/entry.dart'; import 'package:aves/services/common/services.dart'; import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; class MetadataThumbnails extends StatefulWidget { final AvesEntry entry; @@ -44,7 +43,7 @@ class _MetadataThumbnailsState extends State { children: snapshot.data!.map((bytes) { return Image.memory( bytes, - scale: context.select((mq) => mq.devicePixelRatio), + scale: MediaQuery.devicePixelRatioOf(context), ); }).toList(), ), diff --git a/lib/widgets/viewer/overlay/bottom.dart b/lib/widgets/viewer/overlay/bottom.dart index d80348978..cb9339f2a 100644 --- a/lib/widgets/viewer/overlay/bottom.dart +++ b/lib/widgets/viewer/overlay/bottom.dart @@ -20,7 +20,6 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; -import 'package:tuple/tuple.dart'; class ViewerBottomOverlay extends StatefulWidget { final List entries; @@ -297,19 +296,14 @@ class ExtraBottomOverlay extends StatelessWidget { @override Widget build(BuildContext context) { - final mq = context.select>((mq) => Tuple3(mq.size.width, mq.viewInsets, mq.viewPadding)); - final mqWidth = mq.item1; - final mqViewInsets = mq.item2; - final mqViewPadding = mq.item3; - - final viewInsets = this.viewInsets ?? mqViewInsets; - final viewPadding = this.viewPadding ?? mqViewPadding; + final viewInsets = this.viewInsets ?? MediaQuery.viewInsetsOf(context); + final viewPadding = this.viewPadding ?? MediaQuery.viewPaddingOf(context); final safePadding = (viewInsets + viewPadding).copyWith(bottom: 8) + const EdgeInsets.symmetric(horizontal: 8.0); return Padding( padding: safePadding, child: SizedBox( - width: mqWidth - safePadding.horizontal, + width: MediaQuery.sizeOf(context).width - safePadding.horizontal, child: child, ), ); diff --git a/lib/widgets/viewer/overlay/details/date.dart b/lib/widgets/viewer/overlay/details/date.dart index 62abba441..e6e77fb55 100644 --- a/lib/widgets/viewer/overlay/details/date.dart +++ b/lib/widgets/viewer/overlay/details/date.dart @@ -9,7 +9,6 @@ import 'package:aves/widgets/viewer/multipage/controller.dart'; import 'package:aves/widgets/viewer/overlay/details/details.dart'; import 'package:decorated_icon/decorated_icon.dart'; import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; class OverlayDateRow extends StatelessWidget { final AvesEntry entry; @@ -24,7 +23,7 @@ class OverlayDateRow extends StatelessWidget { @override Widget build(BuildContext context) { final locale = context.l10n.localeName; - final use24hour = context.select((v) => v.alwaysUse24HourFormat); + final use24hour = MediaQuery.alwaysUse24HourFormatOf(context); final date = entry.bestDate; final dateText = date != null ? formatDateTime(date, locale, use24hour) : AText.valueNotAvailable; diff --git a/lib/widgets/viewer/overlay/details/details.dart b/lib/widgets/viewer/overlay/details/details.dart index c7b4bba56..b84fc33f4 100644 --- a/lib/widgets/viewer/overlay/details/details.dart +++ b/lib/widgets/viewer/overlay/details/details.dart @@ -150,81 +150,79 @@ class ViewerDetailOverlayContent extends StatelessWidget { @override Widget build(BuildContext context) { + return AnimatedBuilder( + animation: pageEntry.metadataChangeNotifier, + builder: (context, child) => DefaultTextStyle( + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + shadows: shadows(context), + ), + softWrap: false, + overflow: TextOverflow.fade, + maxLines: 1, + child: Padding( + padding: padding, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: _buildRows(context), + ), + ), + ), + ); + } + + List _buildRows(BuildContext context) { final infoMaxWidth = availableWidth - padding.horizontal; final showRatingTags = settings.showOverlayRatingTags; final showShootingDetails = settings.showOverlayShootingDetails; final showDescription = settings.showOverlayDescription; - return AnimatedBuilder( - animation: pageEntry.metadataChangeNotifier, - builder: (context, child) { - final positionTitle = OverlayPositionTitleRow( - entry: pageEntry, - collectionPosition: position, - multiPageController: multiPageController, - ); - return DefaultTextStyle( - style: Theme.of(context).textTheme.bodyMedium!.copyWith( - shadows: shadows(context), - ), - softWrap: false, - overflow: TextOverflow.fade, - maxLines: 1, - child: Padding( - padding: padding, - child: Selector( - selector: (context, mq) => mq.orientation, - builder: (context, orientation, child) { - final twoColumns = orientation == Orientation.landscape && infoMaxWidth / 2 > _subRowMinWidth; - final subRowWidth = twoColumns ? min(_subRowMinWidth, infoMaxWidth / 2) : infoMaxWidth; - final collapsedShooting = twoColumns && showShootingDetails; - final collapsedLocation = twoColumns && !showShootingDetails; + final isLandscape = MediaQuery.orientationOf(context) == Orientation.landscape; + final twoColumns = isLandscape && infoMaxWidth / 2 > _subRowMinWidth; + final subRowWidth = twoColumns ? min(_subRowMinWidth, infoMaxWidth / 2) : infoMaxWidth; + final collapsedShooting = twoColumns && showShootingDetails; + final collapsedLocation = twoColumns && !showShootingDetails; - final rows = []; - if (positionTitle.isNotEmpty) { - rows.add(OverlayRowExpander( - expandedNotifier: expandedNotifier, - child: positionTitle, - )); - rows.add(const SizedBox(height: _interRowPadding)); - } - if (twoColumns) { - rows.add( - Row( - children: [ - _buildDateSubRow(subRowWidth), - if (collapsedShooting) _buildShootingSubRow(context, subRowWidth), - if (collapsedLocation) _buildLocationSubRow(context, subRowWidth), - ], - ), - ); - } else { - rows.add(_buildDateSubRow(subRowWidth)); - if (showShootingDetails) { - rows.add(_buildShootingFullRow(context, subRowWidth)); - } - } - if (!collapsedLocation) { - rows.add(_buildLocationFullRow(context)); - } - if (showRatingTags) { - rows.add(_buildRatingTagsFullRow(context)); - } - if (showDescription) { - rows.add(_buildDescriptionFullRow(context)); - } - - return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: rows, - ); - }, - ), - ), - ); - }, + final positionTitle = OverlayPositionTitleRow( + entry: pageEntry, + collectionPosition: position, + multiPageController: multiPageController, ); + + final rows = []; + if (positionTitle.isNotEmpty) { + rows.add(OverlayRowExpander( + expandedNotifier: expandedNotifier, + child: positionTitle, + )); + rows.add(const SizedBox(height: _interRowPadding)); + } + if (twoColumns) { + rows.add( + Row( + children: [ + _buildDateSubRow(subRowWidth), + if (collapsedShooting) _buildShootingSubRow(context, subRowWidth), + if (collapsedLocation) _buildLocationSubRow(context, subRowWidth), + ], + ), + ); + } else { + rows.add(_buildDateSubRow(subRowWidth)); + if (showShootingDetails) { + rows.add(_buildShootingFullRow(context, subRowWidth)); + } + } + if (!collapsedLocation) { + rows.add(_buildLocationFullRow(context)); + } + if (showRatingTags) { + rows.add(_buildRatingTagsFullRow(context)); + } + if (showDescription) { + rows.add(_buildDescriptionFullRow(context)); + } + return rows; } Widget _buildDateSubRow(double subRowWidth) => SizedBox( diff --git a/lib/widgets/viewer/overlay/details/rating_tags.dart b/lib/widgets/viewer/overlay/details/rating_tags.dart index 8cda1194d..f2a93d3f5 100644 --- a/lib/widgets/viewer/overlay/details/rating_tags.dart +++ b/lib/widgets/viewer/overlay/details/rating_tags.dart @@ -23,13 +23,10 @@ class OverlayRatingTagsRow extends AnimatedWidget { switch (rating) { case -1: ratingString = context.l10n.filterRatingRejectedLabel; - break; case 0: ratingString = ''; - break; default: ratingString = '${'★' * rating}${'☆' * (5 - rating)}'; - break; } final textScaleFactor = MediaQuery.textScaleFactorOf(context); diff --git a/lib/widgets/viewer/overlay/video/progress_bar.dart b/lib/widgets/viewer/overlay/video/progress_bar.dart index 62a21c974..cdf245690 100644 --- a/lib/widgets/viewer/overlay/video/progress_bar.dart +++ b/lib/widgets/viewer/overlay/video/progress_bar.dart @@ -9,7 +9,6 @@ import 'package:aves/widgets/common/fx/blurred.dart'; import 'package:aves/widgets/common/fx/borders.dart'; import 'package:aves_video/aves_video.dart'; import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; class VideoProgressBar extends StatefulWidget { final AvesVideoController? controller; @@ -155,16 +154,14 @@ class _VideoProgressBarState extends State { Widget _buildMuteIndicator() => StreamBuilder( stream: controller?.volumeStream ?? Stream.value(1.0), builder: (context, snapshot) { + final textScaleFactor = MediaQuery.textScaleFactorOf(context); final isMuted = controller?.isMuted ?? false; return isMuted ? Padding( padding: const EdgeInsetsDirectional.only(end: 8), - child: Selector( - selector: (context, mq) => mq.textScaleFactor, - builder: (context, textScaleFactor, child) => Icon( - AIcons.mute, - size: 16 * textScaleFactor, - ), + child: Icon( + AIcons.mute, + size: 16 * textScaleFactor, ), ) : const SizedBox(); diff --git a/lib/widgets/viewer/overlay/viewer_buttons.dart b/lib/widgets/viewer/overlay/viewer_buttons.dart index fbb31c322..b64a91f6c 100644 --- a/lib/widgets/viewer/overlay/viewer_buttons.dart +++ b/lib/widgets/viewer/overlay/viewer_buttons.dart @@ -327,19 +327,14 @@ class ViewerButtonRowContent extends StatelessWidget { switch (action) { case EntryAction.videoCaptureFrame: enabled = videoController?.canCaptureFrameNotifier.value ?? false; - break; case EntryAction.videoToggleMute: enabled = videoController?.canMuteNotifier.value ?? false; - break; case EntryAction.videoSelectStreams: enabled = videoController?.canSelectStreamNotifier.value ?? false; - break; case EntryAction.videoSetSpeed: enabled = videoController?.canSetSpeedNotifier.value ?? false; - break; default: enabled = true; - break; } Widget? child; @@ -349,22 +344,18 @@ class ViewerButtonRowContent extends StatelessWidget { entries: {favouriteTargetEntry}, isMenuItem: true, ); - break; case EntryAction.videoToggleMute: child = MuteToggler( controller: videoController, isMenuItem: true, ); - break; case EntryAction.videoTogglePlay: child = PlayToggler( controller: videoController, isMenuItem: true, ); - break; default: child = MenuRow(text: action.getText(context), icon: action.getIcon()); - break; } return PopupMenuItem( value: action, @@ -448,7 +439,6 @@ class ViewerButtonRowContent extends StatelessWidget { onChooserValue: (album) => actionDelegate.quickMove(context, album, copy: true), onPressed: onPressed, ); - break; case EntryAction.move: child = MoveButton( copy: false, @@ -456,7 +446,6 @@ class ViewerButtonRowContent extends StatelessWidget { onChooserValue: (album) => actionDelegate.quickMove(context, album, copy: false), onPressed: onPressed, ); - break; case EntryAction.share: child = ShareButton( blurred: blurred, @@ -465,7 +454,6 @@ class ViewerButtonRowContent extends StatelessWidget { focusNode: focusNode, onPressed: onPressed, ); - break; case EntryAction.toggleFavourite: final favouriteTargetEntry = mainEntry.isBurst ? pageEntry : mainEntry; child = FavouriteToggler( @@ -473,30 +461,24 @@ class ViewerButtonRowContent extends StatelessWidget { focusNode: focusNode, onPressed: onPressed, ); - break; case EntryAction.videoToggleMute: child = MuteToggler( controller: videoController, focusNode: focusNode, onPressed: onPressed, ); - break; case EntryAction.videoTogglePlay: child = PlayToggler( controller: videoController, focusNode: focusNode, onPressed: onPressed, ); - break; case EntryAction.videoCaptureFrame: child = _buildFromListenable(videoController?.canCaptureFrameNotifier); - break; case EntryAction.videoSelectStreams: child = _buildFromListenable(videoController?.canSelectStreamNotifier); - break; case EntryAction.videoSetSpeed: child = _buildFromListenable(videoController?.canSetSpeedNotifier); - break; case EntryAction.editRating: child = RateButton( blurred: blurred, @@ -504,7 +486,6 @@ class ViewerButtonRowContent extends StatelessWidget { focusNode: focusNode, onPressed: onPressed, ); - break; case EntryAction.editTags: child = TagButton( blurred: blurred, @@ -512,7 +493,6 @@ class ViewerButtonRowContent extends StatelessWidget { focusNode: focusNode, onPressed: onPressed, ); - break; default: child = IconButton( icon: action.getIcon(), @@ -520,7 +500,6 @@ class ViewerButtonRowContent extends StatelessWidget { focusNode: focusNode, tooltip: action.getText(context), ); - break; } return child; } diff --git a/lib/widgets/viewer/overlay/wallpaper_buttons.dart b/lib/widgets/viewer/overlay/wallpaper_buttons.dart index 2ff880700..fdec17399 100644 --- a/lib/widgets/viewer/overlay/wallpaper_buttons.dart +++ b/lib/widgets/viewer/overlay/wallpaper_buttons.dart @@ -209,14 +209,11 @@ class WallpaperButtons extends StatelessWidget with FeedbackMixin { case 90: canvas.translate(-imageDisplaySize.width, imageDisplaySize.height); canvas.rotate(degToRadian(270)); - break; case 180: canvas.translate(0, imageDisplaySize.height); canvas.rotate(degToRadian(180)); - break; case 270: canvas.rotate(degToRadian(90)); - break; } } else { switch (rotationDegrees) { @@ -224,13 +221,10 @@ class WallpaperButtons extends StatelessWidget with FeedbackMixin { canvas.translate(imageDisplaySize.width, 0); cropDx = -displayRegion.top.toDouble(); cropDy = displayRegion.left.toDouble(); - break; case 180: canvas.translate(imageDisplaySize.width, imageDisplaySize.height); - break; case 270: canvas.translate(0, imageDisplaySize.height); - break; } if (rotationDegrees != 0) { canvas.rotate(degToRadian(rotationDegrees.toDouble())); diff --git a/lib/widgets/viewer/panorama_page.dart b/lib/widgets/viewer/panorama_page.dart index 71067e16d..464dc5dc8 100644 --- a/lib/widgets/viewer/panorama_page.dart +++ b/lib/widgets/viewer/panorama_page.dart @@ -156,11 +156,9 @@ class _PanoramaPageState extends State { switch (_sensorControl.value) { case SensorControl.None: _sensorControl.value = SensorControl.AbsoluteOrientation; - break; case SensorControl.AbsoluteOrientation: case SensorControl.Orientation: _sensorControl.value = SensorControl.None; - break; } } diff --git a/lib/widgets/viewer/slideshow_page.dart b/lib/widgets/viewer/slideshow_page.dart index d4139d669..d05186d6f 100644 --- a/lib/widgets/viewer/slideshow_page.dart +++ b/lib/widgets/viewer/slideshow_page.dart @@ -124,13 +124,10 @@ class _SlideshowPageState extends State { switch (action) { case SlideshowAction.resume: _viewerController.autopilot = true; - break; case SlideshowAction.showInCollection: _showInCollection(); - break; case SlideshowAction.settings: _showSettings(context); - break; } } diff --git a/lib/widgets/viewer/video/fijkplayer.dart b/lib/widgets/viewer/video/fijkplayer.dart index bd373c087..80d65934e 100644 --- a/lib/widgets/viewer/video/fijkplayer.dart +++ b/lib/widgets/viewer/video/fijkplayer.dart @@ -270,13 +270,10 @@ class IjkPlayerAvesVideoController extends AvesVideoController { if (width != null && height != null) { videoStreamCount++; } - break; case MediaStreamType.audio: audioStreamCount++; - break; case MediaStreamType.text: textStreamCount++; - break; } } }); @@ -303,9 +300,6 @@ class IjkPlayerAvesVideoController extends AvesVideoController { if (fields == null) return; final event = fields['event'] as String?; switch (event) { - case 'volume': - // ignore - break; case 'audiofocus': final value = fields['value'] as int?; if (value != null) { @@ -313,9 +307,10 @@ class IjkPlayerAvesVideoController extends AvesVideoController { case _audioFocusLoss: case _audioFocusRequestFailed: pause(); - break; } } + case 'volume': + // ignore break; } } @@ -538,11 +533,9 @@ extension ExtraFijkPlayer on FijkPlayer { case FijkState.prepared: removeListener(onChanged); completer.complete(); - break; case FijkState.error: removeListener(onChanged); completer.completeError(value.exception); - break; default: break; } diff --git a/lib/widgets/viewer/video/flutter_vlc_player.dart b/lib/widgets/viewer/video/flutter_vlc_player.dart index f9de65fbe..6fb16fe06 100644 --- a/lib/widgets/viewer/video/flutter_vlc_player.dart +++ b/lib/widgets/viewer/video/flutter_vlc_player.dart @@ -83,7 +83,7 @@ // // do not use `Magnifier` with `applyScale` enabled when using this widget, // // as the original video size will be used to create the `PlatformView` // // and a virtual display larger than the device screen may crash the app -// final mqWidth = context.select((mq) => mq.size.width); +// final mqWidth = MediaQuery.sizeOf(context).width; // final displaySize = entry.displaySize; // final ratio = mqWidth / displaySize.width; // return SizedBox.fromSize( diff --git a/lib/widgets/viewer/visual/entry_page_view.dart b/lib/widgets/viewer/visual/entry_page_view.dart index 30bf70574..fd1cfe61f 100644 --- a/lib/widgets/viewer/visual/entry_page_view.dart +++ b/lib/widgets/viewer/visual/entry_page_view.dart @@ -419,16 +419,12 @@ class _EntryPageViewState extends State with SingleTickerProvider switch (direction) { case AxisDirection.left: const ShowPreviousEntryNotification(animate: true).dispatch(context); - break; case AxisDirection.right: const ShowNextEntryNotification(animate: true).dispatch(context); - break; case AxisDirection.up: PopVisualNotification().dispatch(context); - break; case AxisDirection.down: ShowInfoPageNotification().dispatch(context); - break; } } @@ -456,24 +452,18 @@ class _EntryPageViewState extends State with SingleTickerProvider switch (event.command) { case MediaCommand.play: videoController.play(); - break; case MediaCommand.pause: videoController.pause(); - break; case MediaCommand.skipToNext: ShowNextVideoNotification().dispatch(context); - break; case MediaCommand.skipToPrevious: ShowPreviousVideoNotification().dispatch(context); - break; case MediaCommand.stop: videoController.pause(); - break; case MediaCommand.seek: if (event is MediaSeekCommandEvent) { videoController.seekTo(event.position); } - break; } } @@ -492,14 +482,8 @@ class _EntryPageViewState extends State with SingleTickerProvider } double? _getSideRatio() { - switch (context.read()?.orientation) { - case Orientation.portrait: - return 1 / 5; - case Orientation.landscape: - return 1 / 8; - case null: - return null; - } + final isPortrait = MediaQuery.orientationOf(context) == Orientation.portrait; + return isPortrait ? 1 / 5 : 1 / 8; } static ScaleState _vectorScaleStateCycle(ScaleState actual) { diff --git a/lib/widgets/viewer/visual/vector.dart b/lib/widgets/viewer/visual/vector.dart index 5a198cc43..19503373b 100644 --- a/lib/widgets/viewer/visual/vector.dart +++ b/lib/widgets/viewer/visual/vector.dart @@ -1,5 +1,4 @@ import 'dart:math'; -import 'dart:ui'; import 'package:aves/image_providers/region_provider.dart'; import 'package:aves/model/entry/entry.dart'; @@ -103,12 +102,18 @@ class _VectorImageViewState extends State { Widget build(BuildContext context) { if (_displaySize == Size.zero) return widget.errorBuilder(context, 'Not sized', null); + final devicePixelRatio = View.of(context).devicePixelRatio; return ValueListenableBuilder( valueListenable: viewStateNotifier, builder: (context, viewState, child) { final viewportSize = viewState.viewportSize; final viewportSized = viewportSize?.isEmpty == false; - if (viewportSized && !_isTilingInitialized) _initTiling(viewportSize!); + if (viewportSized && !_isTilingInitialized) { + _initTiling( + viewportSize: viewportSize!, + devicePixelRatio: devicePixelRatio, + ); + } return SizedBox.fromSize( size: _displaySize * viewState.scale!, @@ -116,7 +121,7 @@ class _VectorImageViewState extends State { alignment: Alignment.center, children: [ _buildLoading(), - ..._getTiles(), + ..._getTiles(devicePixelRatio), ], ), ); @@ -124,11 +129,14 @@ class _VectorImageViewState extends State { ); } - void _initTiling(Size viewportSize) { + void _initTiling({ + required Size viewportSize, + required double devicePixelRatio, + }) { _tileSide = _displaySize.longestSide; // scale for initial state `contained` final containedScale = min(viewportSize.width / _displaySize.width, viewportSize.height / _displaySize.height); - _minScale = _imageScaleForViewScale(containedScale); + _minScale = _imageScaleForViewScale(scale: containedScale, devicePixelRatio: devicePixelRatio); _isTilingInitialized = true; _registerFullImage(); @@ -154,7 +162,7 @@ class _VectorImageViewState extends State { ); } - List _getTiles() { + List _getTiles(double devicePixelRatio) { if (!_isTilingInitialized) return []; final displayWidth = _displaySize.width; @@ -205,7 +213,7 @@ class _VectorImageViewState extends State { ); final tiles = [fullImageRegionTile]; - final maxSvgScale = max(_imageScaleForViewScale(viewScale), _minScale); + final maxSvgScale = max(_imageScaleForViewScale(scale: viewScale, devicePixelRatio: devicePixelRatio), _minScale); double nextScale(double scale) => scale * 2; // add `alpha` to the region side so that tiles do not align across layers, // which helps the checkered background deflation workaround @@ -273,7 +281,11 @@ class _VectorImageViewState extends State { return Tuple2>(tileRect, regionRect); } - double _imageScaleForViewScale(double scale) => smallestPowerOf2(scale * window.devicePixelRatio).toDouble(); + double _imageScaleForViewScale({ + required double scale, + required double devicePixelRatio, + }) => + smallestPowerOf2(scale * devicePixelRatio).toDouble(); } typedef _BackgroundFrameBuilder = Widget Function(Widget child, int? frame, Rect tileRect); diff --git a/lib/widgets/viewer/visual/video/cover.dart b/lib/widgets/viewer/visual/video/cover.dart index 6091cbd69..d3438159d 100644 --- a/lib/widgets/viewer/visual/video/cover.dart +++ b/lib/widgets/viewer/visual/video/cover.dart @@ -144,6 +144,7 @@ class _VideoCoverState extends State { child: ThumbnailImage( entry: entry, extent: extent, + devicePixelRatio: MediaQuery.devicePixelRatioOf(context), fit: BoxFit.contain, showLoadingBackground: false, ), diff --git a/lib/widgets/viewer/visual/video/subtitle/ass_parser.dart b/lib/widgets/viewer/visual/video/subtitle/ass_parser.dart index e6b78b844..06bba15df 100644 --- a/lib/widgets/viewer/visual/video/subtitle/ass_parser.dart +++ b/lib/widgets/viewer/visual/video/subtitle/ass_parser.dart @@ -88,21 +88,18 @@ class AssParser { borderColor: extraStyle.borderColor?.withAlpha(a), ); } - break; } case '1a': { // \1a: fill alpha final a = _parseAlpha(param); if (a != null) textStyle = textStyle.copyWith(color: textStyle.color?.withAlpha(a)); - break; } case '3a': { // \3a: border alpha final a = _parseAlpha(param); if (a != null) extraStyle = extraStyle.copyWith(borderColor: extraStyle.borderColor?.withAlpha(a)); - break; } case '4a': { @@ -118,43 +115,36 @@ class AssParser { )) .toList()); } - break; } case 'a': // \a: line alignment (legacy) extraStyle = _copyWithAlignment(_parseLegacyAlignment(param), extraStyle); - break; case 'an': // \an: line alignment extraStyle = _copyWithAlignment(_parseNewAlignment(param), extraStyle); - break; case 'b': { // \b: bold final weight = _parseFontWeight(param); if (weight != null) textStyle = textStyle.copyWith(fontWeight: weight); - break; } case 'be': { // \be: blurs the edges of the text final times = int.tryParse(param); if (times != null) extraStyle = extraStyle.copyWith(edgeBlur: times == 0 ? 0 : 1); - break; } case 'blur': { // \blur: blurs the edges of the text (Gaussian kernel) final strength = double.tryParse(param); if (strength != null) extraStyle = extraStyle.copyWith(edgeBlur: strength / 2); - break; } case 'bord': { // \bord: border width final size = double.tryParse(param); if (size != null) extraStyle = extraStyle.copyWith(borderWidth: size); - break; } case 'c': case '1c': @@ -164,7 +154,6 @@ class AssParser { if (color != null) { textStyle = textStyle.copyWith(color: color.withAlpha(textStyle.color?.alpha ?? 0xFF)); } - break; } case '3c': { @@ -175,7 +164,6 @@ class AssParser { borderColor: color.withAlpha(extraStyle.borderColor?.alpha ?? 0xFF), ); } - break; } case '4c': { @@ -191,12 +179,10 @@ class AssParser { )) .toList()); } - break; } case 'clip': // \clip: clip (within rectangle or path) line = line.copyWith(clip: _parseClip(param)); - break; case 'fax': { final factor = double.tryParse(param); @@ -204,7 +190,6 @@ class AssParser { if (factor != null && (line.position == null || extraStyle.shearX == null)) { extraStyle = extraStyle.copyWith(shearX: factor); } - break; } case 'fay': { @@ -213,28 +198,24 @@ class AssParser { if (factor != null && (line.position == null || extraStyle.shearY == null)) { extraStyle = extraStyle.copyWith(shearY: factor); } - break; } case 'fn': { final name = param; // TODO TLAD [subtitles] extract fonts from attachment streams, and load these fonts in Flutter if (name.isNotEmpty) textStyle = textStyle.copyWith(fontFamily: name); - break; } case 'frx': { // \frx: text rotation (X axis) final amount = double.tryParse(param); if (amount != null) extraStyle = extraStyle.copyWith(rotationX: amount); - break; } case 'fry': { // \fry: text rotation (Y axis) final amount = double.tryParse(param); if (amount != null) extraStyle = extraStyle.copyWith(rotationY: amount); - break; } case 'fr': case 'frz': @@ -242,40 +223,34 @@ class AssParser { // \frz: text rotation (Z axis) final amount = double.tryParse(param); if (amount != null) extraStyle = extraStyle.copyWith(rotationZ: amount); - break; } case 'fs': { // \fs: font size final size = int.tryParse(param); if (size != null) textStyle = textStyle.copyWith(fontSize: size * scale); - break; } case 'fscx': { // \fscx: font scale (horizontal) final scale = int.tryParse(param); if (scale != null) extraStyle = extraStyle.copyWith(scaleX: scale.toDouble() / 100); - break; } case 'fscy': { // \fscx: font scale (vertical) final scale = int.tryParse(param); if (scale != null) extraStyle = extraStyle.copyWith(scaleY: scale.toDouble() / 100); - break; } case 'fsp': { // \fsp: letter spacing final spacing = double.tryParse(param); textStyle = textStyle.copyWith(letterSpacing: spacing); - break; } case 'i': // \i: italics textStyle = textStyle.copyWith(fontStyle: param == '1' ? FontStyle.italic : FontStyle.normal); - break; case 'p': { // \p drawing paths @@ -290,7 +265,6 @@ class AssParser { extraStyle = extraStyle.copyWith(drawingPaths: null); } } - break; } case 'pos': { @@ -309,20 +283,16 @@ class AssParser { } } } - break; } case 'r': // \r: reset textStyle = baseStyle; - break; case 's': // \s: strikeout textStyle = textStyle.copyWith(decoration: param == '1' ? TextDecoration.lineThrough : TextDecoration.none); - break; case 'u': // \u: underline textStyle = textStyle.copyWith(decoration: param == '1' ? TextDecoration.underline : TextDecoration.none); - break; // TODO TLAD [subtitles] SHOULD support the following case 'shad': case 't': // \t: animated transform @@ -512,13 +482,11 @@ class AssParser { path!.cubicTo(points[0], points[1], points[2], points[3], points[4], points[5]); } } - break; case 'c': if (path != null) { path!.close(); } path = null; - break; case 'l': if (path != null) { const lParamCount = 2; @@ -528,7 +496,6 @@ class AssParser { path!.lineTo(points[0], points[1]); } } - break; case 'm': if (params.length == 2) { if (path != null) { @@ -538,16 +505,13 @@ class AssParser { paths.add(path!); path!.moveTo(params[0], params[1]); } - break; case 'n': if (params.length == 2 && path != null) { path!.moveTo(params[0], params[1]); } - break; case 's': case 'p': debugPrint('unhandled ASS drawing command=$command'); - break; } } }); diff --git a/lib/widgets/viewer/visual/video/subtitle/subtitle.dart b/lib/widgets/viewer/visual/video/subtitle/subtitle.dart index 48919273c..bbf86b845 100644 --- a/lib/widgets/viewer/visual/video/subtitle/subtitle.dart +++ b/lib/widgets/viewer/visual/video/subtitle/subtitle.dart @@ -4,11 +4,11 @@ import 'package:aves/model/settings/enums/subtitle_position.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/widgets/common/basic/text/background_painter.dart'; import 'package:aves/widgets/common/basic/text/outlined.dart'; -import 'package:aves_video/aves_video.dart'; import 'package:aves/widgets/viewer/visual/state.dart'; import 'package:aves/widgets/viewer/visual/video/subtitle/ass_parser.dart'; import 'package:aves/widgets/viewer/visual/video/subtitle/span.dart'; import 'package:aves/widgets/viewer/visual/video/subtitle/style.dart'; +import 'package:aves_video/aves_video.dart'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; @@ -53,234 +53,217 @@ class VideoSubtitles extends StatelessWidget { shadows: settings.subtitleShowOutline ? baseShadows : null, ); - return Selector( - selector: (context, mq) => mq.orientation, - builder: (context, orientation, child) { - final bottom = orientation == Orientation.portrait ? .5 : .8; - final viewportSize = context.read().size; + final viewportSize = MediaQuery.sizeOf(context); + final isPortrait = MediaQuery.orientationOf(context) == Orientation.portrait; + final bottom = isPortrait ? .5 : .8; + return ValueListenableBuilder( + valueListenable: viewStateNotifier, + builder: (context, viewState, child) { + final viewPosition = viewState.position; + final viewScale = viewState.scale ?? 1; + final viewSize = videoDisplaySize * viewScale; + final viewOffset = Offset( + (viewportSize.width - viewSize.width) / 2, + (viewportSize.height - viewSize.height) / 2, + ); - return ValueListenableBuilder( - valueListenable: viewStateNotifier, - builder: (context, viewState, child) { - final viewPosition = viewState.position; - final viewScale = viewState.scale ?? 1; - final viewSize = videoDisplaySize * viewScale; - final viewOffset = Offset( - (viewportSize.width - viewSize.width) / 2, - (viewportSize.height - viewSize.height) / 2, - ); + return StreamBuilder( + stream: controller.timedTextStream, + builder: (context, snapshot) { + final text = snapshot.data; + if (text == null) return const SizedBox(); - return StreamBuilder( - stream: controller.timedTextStream, - builder: (context, snapshot) { - final text = snapshot.data; - if (text == null) return const SizedBox(); + if (debugMode) { + return Padding( + padding: const EdgeInsets.only(top: 100.0), + child: Align( + alignment: Alignment.topLeft, + child: OutlinedText( + textSpans: [ + TextSpan( + text: text, + style: const TextStyle(fontSize: 14), + ) + ], + outlineWidth: 1, + outlineColor: Colors.black, + ), + ), + ); + } - if (debugMode) { - return Padding( - padding: const EdgeInsets.only(top: 100.0), + final styledLine = AssParser.parse(text, baseStyle, viewScale); + final position = styledLine.position; + final clip = styledLine.clip; + final styledSpans = styledLine.spans; + final byExtraStyle = groupBy(styledSpans, (v) => v.extraStyle); + return Stack( + children: byExtraStyle.entries.map((kv) { + final extraStyle = kv.key; + final spans = kv.value.map((v) { + final span = v.textSpan; + final style = span.style; + if (position == null || style == null) return span; + + final letterSpacing = style.letterSpacing; + final shadows = style.shadows; + return TextSpan( + text: span.text, + style: style.copyWith( + letterSpacing: letterSpacing != null ? letterSpacing * viewScale : null, + shadows: shadows + ?.map((v) => Shadow( + color: v.color, + offset: v.offset * viewScale, + blurRadius: v.blurRadius * viewScale, + )) + .toList(), + ), + ); + }).toList(); + final drawingPaths = extraStyle.drawingPaths; + final textHAlign = extraStyle.hAlign ?? (position != null ? TextAlign.center : baseTextAlign); + final textVAlign = extraStyle.vAlign ?? (position != null ? TextAlignVertical.bottom : baseTextAlignY); + + Widget child; + if (drawingPaths != null) { + child = CustomPaint( + painter: SubtitlePathPainter( + paths: drawingPaths, + scale: viewScale, + fillColor: spans.firstOrNull?.style?.color ?? Colors.white, + strokeColor: extraStyle.borderColor, + ), + ); + } else { + final outlineWidth = extraStyle.borderWidth ?? (extraStyle.edgeBlur != null ? 2 : 1); + child = OutlinedText( + textSpans: spans, + outlineWidth: outlineWidth * (position != null ? viewScale : baseOutlineWidth), + outlineColor: extraStyle.borderColor ?? baseOutlineColor, + outlineBlurSigma: extraStyle.edgeBlur ?? 0, + textAlign: textHAlign, + ); + } + + var transform = Matrix4.identity(); + + if (position != null) { + final para = RenderParagraph( + TextSpan(children: spans), + textDirection: TextDirection.ltr, + textScaleFactor: MediaQuery.textScaleFactorOf(context), + )..layout(const BoxConstraints()); + final textWidth = para.getMaxIntrinsicWidth(double.infinity); + final textHeight = para.getMaxIntrinsicHeight(double.infinity); + + late double anchorOffsetX, anchorOffsetY; + switch (textHAlign) { + case TextAlign.left: + anchorOffsetX = 0; + case TextAlign.right: + anchorOffsetX = -textWidth; + case TextAlign.center: + default: + anchorOffsetX = -textWidth / 2; + } + switch (textVAlign) { + case TextAlignVertical.top: + anchorOffsetY = 0; + case TextAlignVertical.center: + anchorOffsetY = -textHeight / 2; + case TextAlignVertical.bottom: + anchorOffsetY = -textHeight; + } + final alignOffset = Offset(anchorOffsetX, anchorOffsetY); + final lineOffset = position * viewScale + viewPosition; + final translateOffset = viewOffset + lineOffset + alignOffset; + transform.translate(translateOffset.dx, translateOffset.dy); + } + + if (extraStyle.rotating) { + // for perspective + transform.setEntry(3, 2, 0.001); + final x = -angles.degToRadian(extraStyle.rotationX ?? 0); + final y = -angles.degToRadian(extraStyle.rotationY ?? 0); + final z = -angles.degToRadian(extraStyle.rotationZ ?? 0); + if (x != 0) transform.rotateX(x); + if (y != 0) transform.rotateY(y); + if (z != 0) transform.rotateZ(z); + } + if (extraStyle.scaling) { + final x = extraStyle.scaleX ?? 1; + final y = extraStyle.scaleY ?? 1; + transform.scale(x, y); + } + if (extraStyle.shearing) { + final x = extraStyle.shearX ?? 0; + final y = extraStyle.shearY ?? 0; + transform.multiply(Matrix4(1, y, 0, 0, x, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)); + } + + if (!transform.isIdentity()) { + child = Transform( + transform: transform, + alignment: Alignment.center, + child: child, + ); + } + + if (position == null) { + late double alignX; + switch (textHAlign) { + case TextAlign.left: + alignX = -1; + case TextAlign.right: + alignX = 1; + case TextAlign.center: + default: + alignX = 0; + } + late double alignY; + switch (textVAlign) { + case TextAlignVertical.top: + alignY = -bottom; + case TextAlignVertical.center: + alignY = 0; + case TextAlignVertical.bottom: + default: + alignY = bottom; + } + child = Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), child: Align( - alignment: Alignment.topLeft, - child: OutlinedText( - textSpans: [ - TextSpan( - text: text, - style: const TextStyle(fontSize: 14), - ) - ], - outlineWidth: 1, - outlineColor: Colors.black, + alignment: Alignment(alignX, alignY), + child: TextBackgroundPainter( + spans: spans, + style: DefaultTextStyle.of(context).style.merge(spans.first.style!.copyWith( + backgroundColor: settings.subtitleBackgroundColor, + )), + textAlign: textHAlign, + child: child, ), ), ); } - final styledLine = AssParser.parse(text, baseStyle, viewScale); - final position = styledLine.position; - final clip = styledLine.clip; - final styledSpans = styledLine.spans; - final byExtraStyle = groupBy(styledSpans, (v) => v.extraStyle); - return Stack( - children: byExtraStyle.entries.map((kv) { - final extraStyle = kv.key; - final spans = kv.value.map((v) { - final span = v.textSpan; - final style = span.style; - if (position == null || style == null) return span; + if (clip != null) { + final clipOffset = viewOffset + viewPosition; + final matrix = Matrix4.identity() + ..translate(clipOffset.dx, clipOffset.dy) + ..scale(viewScale, viewScale); + final transform = matrix.storage; + child = ClipPath( + clipper: SubtitlePathClipper( + paths: clip.map((v) => v.transform(transform)).toList(), + scale: viewScale, + ), + child: child, + ); + } - final letterSpacing = style.letterSpacing; - final shadows = style.shadows; - return TextSpan( - text: span.text, - style: style.copyWith( - letterSpacing: letterSpacing != null ? letterSpacing * viewScale : null, - shadows: shadows - ?.map((v) => Shadow( - color: v.color, - offset: v.offset * viewScale, - blurRadius: v.blurRadius * viewScale, - )) - .toList(), - ), - ); - }).toList(); - final drawingPaths = extraStyle.drawingPaths; - final textHAlign = extraStyle.hAlign ?? (position != null ? TextAlign.center : baseTextAlign); - final textVAlign = extraStyle.vAlign ?? (position != null ? TextAlignVertical.bottom : baseTextAlignY); - - Widget child; - if (drawingPaths != null) { - child = CustomPaint( - painter: SubtitlePathPainter( - paths: drawingPaths, - scale: viewScale, - fillColor: spans.firstOrNull?.style?.color ?? Colors.white, - strokeColor: extraStyle.borderColor, - ), - ); - } else { - final outlineWidth = extraStyle.borderWidth ?? (extraStyle.edgeBlur != null ? 2 : 1); - child = OutlinedText( - textSpans: spans, - outlineWidth: outlineWidth * (position != null ? viewScale : baseOutlineWidth), - outlineColor: extraStyle.borderColor ?? baseOutlineColor, - outlineBlurSigma: extraStyle.edgeBlur ?? 0, - textAlign: textHAlign, - ); - } - - var transform = Matrix4.identity(); - - if (position != null) { - final para = RenderParagraph( - TextSpan(children: spans), - textDirection: TextDirection.ltr, - textScaleFactor: context.read().textScaleFactor, - )..layout(const BoxConstraints()); - final textWidth = para.getMaxIntrinsicWidth(double.infinity); - final textHeight = para.getMaxIntrinsicHeight(double.infinity); - - late double anchorOffsetX, anchorOffsetY; - switch (textHAlign) { - case TextAlign.left: - anchorOffsetX = 0; - break; - case TextAlign.right: - anchorOffsetX = -textWidth; - break; - case TextAlign.center: - default: - anchorOffsetX = -textWidth / 2; - break; - } - switch (textVAlign) { - case TextAlignVertical.top: - anchorOffsetY = 0; - break; - case TextAlignVertical.center: - anchorOffsetY = -textHeight / 2; - break; - case TextAlignVertical.bottom: - anchorOffsetY = -textHeight; - break; - } - final alignOffset = Offset(anchorOffsetX, anchorOffsetY); - final lineOffset = position * viewScale + viewPosition; - final translateOffset = viewOffset + lineOffset + alignOffset; - transform.translate(translateOffset.dx, translateOffset.dy); - } - - if (extraStyle.rotating) { - // for perspective - transform.setEntry(3, 2, 0.001); - final x = -angles.degToRadian(extraStyle.rotationX ?? 0); - final y = -angles.degToRadian(extraStyle.rotationY ?? 0); - final z = -angles.degToRadian(extraStyle.rotationZ ?? 0); - if (x != 0) transform.rotateX(x); - if (y != 0) transform.rotateY(y); - if (z != 0) transform.rotateZ(z); - } - if (extraStyle.scaling) { - final x = extraStyle.scaleX ?? 1; - final y = extraStyle.scaleY ?? 1; - transform.scale(x, y); - } - if (extraStyle.shearing) { - final x = extraStyle.shearX ?? 0; - final y = extraStyle.shearY ?? 0; - transform.multiply(Matrix4(1, y, 0, 0, x, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)); - } - - if (!transform.isIdentity()) { - child = Transform( - transform: transform, - alignment: Alignment.center, - child: child, - ); - } - - if (position == null) { - late double alignX; - switch (textHAlign) { - case TextAlign.left: - alignX = -1; - break; - case TextAlign.right: - alignX = 1; - break; - case TextAlign.center: - default: - alignX = 0; - break; - } - late double alignY; - switch (textVAlign) { - case TextAlignVertical.top: - alignY = -bottom; - break; - case TextAlignVertical.center: - alignY = 0; - break; - case TextAlignVertical.bottom: - default: - alignY = bottom; - break; - } - child = Padding( - padding: const EdgeInsets.symmetric(horizontal: 8), - child: Align( - alignment: Alignment(alignX, alignY), - child: TextBackgroundPainter( - spans: spans, - style: DefaultTextStyle.of(context).style.merge(spans.first.style!.copyWith( - backgroundColor: settings.subtitleBackgroundColor, - )), - textAlign: textHAlign, - child: child, - ), - ), - ); - } - - if (clip != null) { - final clipOffset = viewOffset + viewPosition; - final matrix = Matrix4.identity() - ..translate(clipOffset.dx, clipOffset.dy) - ..scale(viewScale, viewScale); - final transform = matrix.storage; - child = ClipPath( - clipper: SubtitlePathClipper( - paths: clip.map((v) => v.transform(transform)).toList(), - scale: viewScale, - ), - child: child, - ); - } - - return child; - }).toList(), - ); - }, + return child; + }).toList(), ); }, ); diff --git a/lib/widgets/viewer/visual/video/swipe_action.dart b/lib/widgets/viewer/visual/video/swipe_action.dart index a921cca38..30b049672 100644 --- a/lib/widgets/viewer/visual/video/swipe_action.dart +++ b/lib/widgets/viewer/visual/video/swipe_action.dart @@ -22,10 +22,8 @@ extension ExtraSwipeAction on SwipeAction { switch (this) { case SwipeAction.brightness: await ScreenBrightness().setScreenBrightness(value); - break; case SwipeAction.volume: VolumeController().setVolume(value, showSystemUI: false); - break; } } } diff --git a/lib/widgets/welcome_page.dart b/lib/widgets/welcome_page.dart index a06e1f902..9a03e5a52 100644 --- a/lib/widgets/welcome_page.dart +++ b/lib/widgets/welcome_page.dart @@ -59,7 +59,7 @@ class _WelcomePageState extends State { if (snapshot.hasError || snapshot.connectionState != ConnectionState.done) return const SizedBox(); final terms = snapshot.data!; final durations = context.watch(); - final isPortrait = context.select((mq) => mq.orientation) == Orientation.portrait; + final isPortrait = MediaQuery.orientationOf(context) == Orientation.portrait; return Column( mainAxisSize: MainAxisSize.min, children: _toStaggeredList( diff --git a/plugins/aves_magnifier/lib/src/core/core.dart b/plugins/aves_magnifier/lib/src/core/core.dart index de9829584..d2f95bad9 100644 --- a/plugins/aves_magnifier/lib/src/core/core.dart +++ b/plugins/aves_magnifier/lib/src/core/core.dart @@ -11,7 +11,6 @@ import 'package:aves_magnifier/src/scale/state.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/widgets.dart'; -import 'package:provider/provider.dart'; import 'package:tuple/tuple.dart'; /// Internal widget in which controls all animations lifecycle, core responses @@ -272,7 +271,7 @@ class _MagnifierCoreState extends State with TickerProviderStateM } bool _isFlingGesture(VelocityEstimate estimate, PointerDeviceKind kind, Axis axis) { - final gestureSettings = context.read().gestureSettings; + final gestureSettings = MediaQuery.gestureSettingsOf(context); const minVelocity = kMinFlingVelocity; final minDistance = computeHitSlop(kind, gestureSettings); diff --git a/plugins/aves_magnifier/lib/src/core/gesture_detector.dart b/plugins/aves_magnifier/lib/src/core/gesture_detector.dart index e18c4283f..f28710d4b 100644 --- a/plugins/aves_magnifier/lib/src/core/gesture_detector.dart +++ b/plugins/aves_magnifier/lib/src/core/gesture_detector.dart @@ -3,7 +3,6 @@ import 'package:aves_magnifier/src/pan/edge_hit_detector.dart'; import 'package:aves_magnifier/src/pan/gesture_detector_scope.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/widgets.dart'; -import 'package:provider/provider.dart'; class MagnifierGestureDetector extends StatefulWidget { const MagnifierGestureDetector({ @@ -40,7 +39,7 @@ class _MagnifierGestureDetectorState extends State { @override Widget build(BuildContext context) { - final gestureSettings = context.select((mq) => mq.gestureSettings); + final gestureSettings = MediaQuery.gestureSettingsOf(context); final gestures = {}; if (widget.onTapDown != null || widget.onTapUp != null) { diff --git a/plugins/aves_magnifier/lib/src/core/scale_gesture_recognizer.dart b/plugins/aves_magnifier/lib/src/core/scale_gesture_recognizer.dart index 85b7cfcae..9aa5ebca1 100644 --- a/plugins/aves_magnifier/lib/src/core/scale_gesture_recognizer.dart +++ b/plugins/aves_magnifier/lib/src/core/scale_gesture_recognizer.dart @@ -93,10 +93,8 @@ class MagnifierGestureRecognizer extends ScaleGestureRecognizer { // when it should yield to other recognizers final canAccept = _areMultiPointers() || _isPriorityGesture() || _canPanX() || _canPanY(); super.resolve(canAccept ? GestureDisposition.accepted : GestureDisposition.rejected); - break; case GestureDisposition.rejected: super.resolve(disposition); - break; } } diff --git a/plugins/aves_magnifier/lib/src/scale/scale_boundaries.dart b/plugins/aves_magnifier/lib/src/scale/scale_boundaries.dart index 150bf45bb..256612d9f 100644 --- a/plugins/aves_magnifier/lib/src/scale/scale_boundaries.dart +++ b/plugins/aves_magnifier/lib/src/scale/scale_boundaries.dart @@ -1,10 +1,9 @@ import 'dart:math'; -import 'dart:ui'; import 'package:aves_magnifier/src/controller/controller.dart'; import 'package:aves_magnifier/src/scale/scale_level.dart'; import 'package:equatable/equatable.dart'; -import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; /// Internal class to wrap custom scale boundaries (min, max and initial) /// Also, stores values regarding the two sizes: the container and the child. @@ -58,7 +57,10 @@ class ScaleBoundaries extends Equatable { } } - double get originalScale => 1.0 / window.devicePixelRatio; + double get originalScale { + final view = WidgetsBinding.instance.platformDispatcher.views.firstOrNull; + return 1.0 / (view?.devicePixelRatio ?? 1.0); + } double get minScale => { scaleForLevel(_minScale), diff --git a/plugins/aves_magnifier/pubspec.lock b/plugins/aves_magnifier/pubspec.lock index 03b594497..318c61e2c 100644 --- a/plugins/aves_magnifier/pubspec.lock +++ b/plugins/aves_magnifier/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" collection: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.1" equatable: dependency: "direct main" description: @@ -42,18 +42,18 @@ packages: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.7" lints: dependency: transitive description: name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + sha256: "6b0206b0bf4f04961fc5438198ccb3a885685cd67d4d4a32cc20ad7f8adbe015" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.0" material_color_utilities: dependency: transitive description: @@ -66,10 +66,10 @@ packages: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" nested: dependency: transitive description: @@ -108,5 +108,5 @@ packages: source: hosted version: "2.1.4" sdks: - dart: ">=2.19.6 <3.0.0" + dart: ">=3.0.0 <4.0.0" flutter: ">=1.16.0" diff --git a/plugins/aves_magnifier/pubspec.yaml b/plugins/aves_magnifier/pubspec.yaml index a45d38ae6..b60a8df8c 100644 --- a/plugins/aves_magnifier/pubspec.yaml +++ b/plugins/aves_magnifier/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=2.19.6 <4.0.0" + sdk: ">=3.0.0 <4.0.0" dependencies: flutter: diff --git a/plugins/aves_map/lib/src/marker/generator.dart b/plugins/aves_map/lib/src/marker/generator.dart index 3efff1ebe..ca536b32f 100644 --- a/plugins/aves_map/lib/src/marker/generator.dart +++ b/plugins/aves_map/lib/src/marker/generator.dart @@ -4,7 +4,6 @@ import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; -import 'package:provider/provider.dart'; // generate bitmap from widget, for Google map class MarkerGeneratorWidget extends StatefulWidget { @@ -35,8 +34,12 @@ class _MarkerGeneratorWidgetState extends State oldWidget) { super.didUpdateWidget(oldWidget); + final devicePixelRatio = View.of(context).devicePixelRatio; widget.markers.forEach((markerWidget) { - final item = getOrCreate(markerWidget.key as T); + final item = _getOrCreate( + markerKey: markerWidget.key as T, + devicePixelRatio: devicePixelRatio, + ); item.globalKey = GlobalKey(); }); _checkNextFrame(); @@ -63,7 +66,7 @@ class _MarkerGeneratorWidgetState extends State((mq) => mq.size.width), 0), + offset: Offset(MediaQuery.sizeOf(context).width, 0), child: Material( type: MaterialType.transparency, child: Stack( @@ -78,11 +81,17 @@ class _MarkerGeneratorWidgetState extends State v.markerKey == markerKey); if (existingItem != null) return existingItem; - final newItem = _MarkerGeneratorItem(markerKey); + final newItem = _MarkerGeneratorItem( + markerKey: markerKey, + devicePixelRatio: devicePixelRatio, + ); _items.add(newItem); return newItem; } @@ -92,10 +101,14 @@ enum MarkerGeneratorItemState { waiting, rendering, done } class _MarkerGeneratorItem { final T markerKey; + final double devicePixelRatio; GlobalKey? globalKey; MarkerGeneratorItemState state = MarkerGeneratorItemState.waiting; - _MarkerGeneratorItem(this.markerKey); + _MarkerGeneratorItem({ + required this.markerKey, + required this.devicePixelRatio, + }); bool get isWaiting => state == MarkerGeneratorItemState.waiting; @@ -107,7 +120,7 @@ class _MarkerGeneratorItem { final boundary = _globalKey.currentContext!.findRenderObject() as RenderRepaintBoundary; if (boundary.hasSize && boundary.size != Size.zero) { try { - final image = await boundary.toImage(pixelRatio: ui.window.devicePixelRatio); + final image = await boundary.toImage(pixelRatio: devicePixelRatio); final byteData = await image.toByteData(format: ui.ImageByteFormat.png); bytes = byteData?.buffer.asUint8List(); } catch (error) { diff --git a/plugins/aves_map/pubspec.lock b/plugins/aves_map/pubspec.lock index d19fe7fac..41b0e3bf4 100644 --- a/plugins/aves_map/pubspec.lock +++ b/plugins/aves_map/pubspec.lock @@ -20,10 +20,10 @@ packages: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" clock: dependency: transitive description: @@ -36,10 +36,10 @@ packages: dependency: "direct main" description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.1" custom_rounded_rectangle_border: dependency: "direct main" description: @@ -113,10 +113,10 @@ packages: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.7" latlong2: dependency: "direct main" description: @@ -129,10 +129,10 @@ packages: dependency: transitive description: name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + sha256: "6b0206b0bf4f04961fc5438198ccb3a885685cd67d4d4a32cc20ad7f8adbe015" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.0" lists: dependency: transitive description: @@ -153,10 +153,10 @@ packages: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" mgrs_dart: dependency: transitive description: @@ -246,10 +246,10 @@ packages: dependency: transitive description: name: typed_data - sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" unicode: dependency: transitive description: @@ -275,5 +275,5 @@ packages: source: hosted version: "2.0.0" sdks: - dart: ">=2.19.6 <3.0.0" + dart: ">=3.0.0 <4.0.0" flutter: ">=3.3.0" diff --git a/plugins/aves_map/pubspec.yaml b/plugins/aves_map/pubspec.yaml index 25099293f..a6cdca279 100644 --- a/plugins/aves_map/pubspec.yaml +++ b/plugins/aves_map/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=2.19.6 <4.0.0" + sdk: ">=3.0.0 <4.0.0" dependencies: flutter: diff --git a/plugins/aves_model/pubspec.lock b/plugins/aves_model/pubspec.lock index 206fcaf52..32248f33f 100644 --- a/plugins/aves_model/pubspec.lock +++ b/plugins/aves_model/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" collection: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.1" equatable: dependency: "direct main" description: @@ -42,18 +42,18 @@ packages: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.7" lints: dependency: transitive description: name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + sha256: "6b0206b0bf4f04961fc5438198ccb3a885685cd67d4d4a32cc20ad7f8adbe015" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.0" material_color_utilities: dependency: transitive description: @@ -66,10 +66,10 @@ packages: dependency: "direct main" description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" sky_engine: dependency: transitive description: flutter @@ -84,4 +84,4 @@ packages: source: hosted version: "2.1.4" sdks: - dart: ">=2.19.6 <3.0.0" + dart: ">=3.0.0 <4.0.0" diff --git a/plugins/aves_model/pubspec.yaml b/plugins/aves_model/pubspec.yaml index 4ce8c1284..d24556f14 100644 --- a/plugins/aves_model/pubspec.yaml +++ b/plugins/aves_model/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=2.19.6 <4.0.0" + sdk: ">=3.0.0 <4.0.0" dependencies: flutter: diff --git a/plugins/aves_platform_meta/android/build.gradle b/plugins/aves_platform_meta/android/build.gradle index cded37549..757ddd31e 100644 --- a/plugins/aves_platform_meta/android/build.gradle +++ b/plugins/aves_platform_meta/android/build.gradle @@ -2,14 +2,18 @@ group 'deckers.thibault.aves.aves_platform_meta' version '1.0-SNAPSHOT' buildscript { - ext.kotlin_version = '1.8.0' + ext { + kotlin_version = '1.8.21' + agp_version = '8.0.1' + } + repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.4.2' + classpath "com.android.tools.build:gradle:$agp_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/plugins/aves_platform_meta/android/gradle/wrapper/gradle-wrapper.properties b/plugins/aves_platform_meta/android/gradle/wrapper/gradle-wrapper.properties index cb92fa5fd..34953c675 100644 --- a/plugins/aves_platform_meta/android/gradle/wrapper/gradle-wrapper.properties +++ b/plugins/aves_platform_meta/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip \ No newline at end of file +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip \ No newline at end of file diff --git a/plugins/aves_platform_meta/android/src/main/AndroidManifest.xml b/plugins/aves_platform_meta/android/src/main/AndroidManifest.xml index 36fa2e6a5..cc947c567 100644 --- a/plugins/aves_platform_meta/android/src/main/AndroidManifest.xml +++ b/plugins/aves_platform_meta/android/src/main/AndroidManifest.xml @@ -1,3 +1 @@ - - + diff --git a/plugins/aves_platform_meta/pubspec.lock b/plugins/aves_platform_meta/pubspec.lock index 10b974b12..d5b51cbb3 100644 --- a/plugins/aves_platform_meta/pubspec.lock +++ b/plugins/aves_platform_meta/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" collection: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.1" flutter: dependency: "direct main" description: flutter @@ -34,18 +34,18 @@ packages: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.7" lints: dependency: transitive description: name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + sha256: "6b0206b0bf4f04961fc5438198ccb3a885685cd67d4d4a32cc20ad7f8adbe015" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.0" material_color_utilities: dependency: transitive description: @@ -58,10 +58,10 @@ packages: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" plugin_platform_interface: dependency: "direct main" description: @@ -84,4 +84,4 @@ packages: source: hosted version: "2.1.4" sdks: - dart: ">=2.19.6 <3.0.0" + dart: ">=3.0.0 <4.0.0" diff --git a/plugins/aves_platform_meta/pubspec.yaml b/plugins/aves_platform_meta/pubspec.yaml index 7bfc472b6..c87c30f04 100644 --- a/plugins/aves_platform_meta/pubspec.yaml +++ b/plugins/aves_platform_meta/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=2.19.6 <4.0.0" + sdk: ">=3.0.0 <4.0.0" dependencies: flutter: diff --git a/plugins/aves_report/pubspec.lock b/plugins/aves_report/pubspec.lock index c656d7419..65a37f405 100644 --- a/plugins/aves_report/pubspec.lock +++ b/plugins/aves_report/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" collection: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.1" flutter: dependency: "direct main" description: flutter @@ -34,18 +34,18 @@ packages: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.7" lints: dependency: transitive description: name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + sha256: "6b0206b0bf4f04961fc5438198ccb3a885685cd67d4d4a32cc20ad7f8adbe015" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.0" material_color_utilities: dependency: transitive description: @@ -58,10 +58,10 @@ packages: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" sky_engine: dependency: transitive description: flutter @@ -76,4 +76,4 @@ packages: source: hosted version: "2.1.4" sdks: - dart: ">=2.19.6 <3.0.0" + dart: ">=3.0.0 <4.0.0" diff --git a/plugins/aves_report/pubspec.yaml b/plugins/aves_report/pubspec.yaml index 638daa043..60ad239de 100644 --- a/plugins/aves_report/pubspec.yaml +++ b/plugins/aves_report/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=2.19.6 <4.0.0" + sdk: ">=3.0.0 <4.0.0" dependencies: flutter: diff --git a/plugins/aves_report_console/pubspec.lock b/plugins/aves_report_console/pubspec.lock index 9be92d272..d6ef00fa4 100644 --- a/plugins/aves_report_console/pubspec.lock +++ b/plugins/aves_report_console/pubspec.lock @@ -12,18 +12,18 @@ packages: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" collection: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.1" flutter: dependency: "direct main" description: flutter @@ -41,18 +41,18 @@ packages: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.7" lints: dependency: transitive description: name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + sha256: "6b0206b0bf4f04961fc5438198ccb3a885685cd67d4d4a32cc20ad7f8adbe015" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.0" material_color_utilities: dependency: transitive description: @@ -65,10 +65,10 @@ packages: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" sky_engine: dependency: transitive description: flutter @@ -83,4 +83,4 @@ packages: source: hosted version: "2.1.4" sdks: - dart: ">=2.19.6 <3.0.0" + dart: ">=3.0.0 <4.0.0" diff --git a/plugins/aves_report_console/pubspec.yaml b/plugins/aves_report_console/pubspec.yaml index 337e24999..369f1c8b5 100644 --- a/plugins/aves_report_console/pubspec.yaml +++ b/plugins/aves_report_console/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=2.19.6 <4.0.0" + sdk: ">=3.0.0 <4.0.0" dependencies: flutter: diff --git a/plugins/aves_report_crashlytics/pubspec.lock b/plugins/aves_report_crashlytics/pubspec.lock index bdbe2b15a..716168085 100644 --- a/plugins/aves_report_crashlytics/pubspec.lock +++ b/plugins/aves_report_crashlytics/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: _flutterfire_internals - sha256: "867b77e2367bc502dcd4d5a66302615409f04eb20ed82ba1c0ba073f9107e018" + sha256: d687576bb973e8d2539d0b4615d985336269a0dbe1b3984e937fd22d31406fb8 url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.0" async: dependency: transitive description: name: async - sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.11.0" aves_report: dependency: "direct main" description: @@ -36,10 +36,10 @@ packages: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" clock: dependency: transitive description: @@ -52,10 +52,10 @@ packages: dependency: "direct main" description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.1" fake_async: dependency: transitive description: @@ -68,42 +68,42 @@ packages: dependency: "direct main" description: name: firebase_core - sha256: dcf54c170c5371ad0e79229d0fb372c58262ae0968b7de222bf28e51dd236be0 + sha256: "4491238f4fddc885bc994e304a035eb8aba2c935816b2c0b31d87f3ec6e96682" url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.12.0" firebase_core_platform_interface: dependency: transitive description: name: firebase_core_platform_interface - sha256: ae79f335f6c7f2dadb00c98c429da2ca905d265e0225fb5e7dfa62ac3accad48 + sha256: b63e3be6c96ef5c33bdec1aab23c91eb00696f6452f0519401d640938c94cba2 url: "https://pub.dev" source: hosted - version: "4.7.0" + version: "4.8.0" firebase_core_web: dependency: transitive description: name: firebase_core_web - sha256: e57ef862257a0d977c1308d02e2fbb9b68525e6d85711b08f3df8cec836fb444 + sha256: "8c0f4c87d20e2d001a5915df238c1f9c88704231f591324205f5a5d2a7740a45" url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.5.0" firebase_crashlytics: dependency: "direct main" description: name: firebase_crashlytics - sha256: e8424f75c0fd466a6ae4d069b313ec84133ef275c00a5a3a416b75b99fb3e831 + sha256: cc6cab05d4de3257408c167ec1e2e410f21c0764aea22f78481314496a1cc4ca url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "3.3.0" firebase_crashlytics_platform_interface: dependency: transitive description: name: firebase_crashlytics_platform_interface - sha256: "381e28f33d5255a698ac7755c3c4fdff42382be0154f375564e6852aa46f9d8f" + sha256: b3b6017045866b981b566896a0de6114181bf9e91465221f2e89996f47a160b4 url: "https://pub.dev" source: hosted - version: "3.5.0" + version: "3.6.0" flutter: dependency: "direct main" description: flutter @@ -131,26 +131,26 @@ packages: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.7" lints: dependency: transitive description: name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + sha256: "6b0206b0bf4f04961fc5438198ccb3a885685cd67d4d4a32cc20ad7f8adbe015" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.0" matcher: dependency: transitive description: name: matcher - sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" url: "https://pub.dev" source: hosted - version: "0.12.13" + version: "0.12.15" material_color_utilities: dependency: transitive description: @@ -163,18 +163,18 @@ packages: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" path: dependency: transitive description: name: path - sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" url: "https://pub.dev" source: hosted - version: "1.8.2" + version: "1.8.3" plugin_platform_interface: dependency: transitive description: @@ -232,10 +232,10 @@ packages: dependency: transitive description: name: test_api - sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb url: "https://pub.dev" source: hosted - version: "0.4.16" + version: "0.5.1" vector_math: dependency: transitive description: @@ -245,5 +245,5 @@ packages: source: hosted version: "2.1.4" sdks: - dart: ">=2.19.6 <3.0.0" - flutter: ">=1.20.0" + dart: ">=3.0.0 <4.0.0" + flutter: ">=3.3.0" diff --git a/plugins/aves_report_crashlytics/pubspec.yaml b/plugins/aves_report_crashlytics/pubspec.yaml index 2a4875ff3..acaa8bdca 100644 --- a/plugins/aves_report_crashlytics/pubspec.yaml +++ b/plugins/aves_report_crashlytics/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=2.19.6 <4.0.0" + sdk: ">=3.0.0 <4.0.0" dependencies: flutter: diff --git a/plugins/aves_screen_state/android/build.gradle b/plugins/aves_screen_state/android/build.gradle index 4aae7446c..f8ca69e20 100644 --- a/plugins/aves_screen_state/android/build.gradle +++ b/plugins/aves_screen_state/android/build.gradle @@ -2,14 +2,18 @@ group 'deckers.thibault.aves.aves_screen_state' version '1.0-SNAPSHOT' buildscript { - ext.kotlin_version = '1.8.21' + ext { + kotlin_version = '1.8.21' + agp_version = '8.0.1' + } + repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.4.2' + classpath "com.android.tools.build:gradle:$agp_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/plugins/aves_screen_state/android/gradle/wrapper/gradle-wrapper.properties b/plugins/aves_screen_state/android/gradle/wrapper/gradle-wrapper.properties index 3c472b99c..41681a771 100644 --- a/plugins/aves_screen_state/android/gradle/wrapper/gradle-wrapper.properties +++ b/plugins/aves_screen_state/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip \ No newline at end of file diff --git a/plugins/aves_screen_state/android/src/main/AndroidManifest.xml b/plugins/aves_screen_state/android/src/main/AndroidManifest.xml index 0c066880c..cc947c567 100644 --- a/plugins/aves_screen_state/android/src/main/AndroidManifest.xml +++ b/plugins/aves_screen_state/android/src/main/AndroidManifest.xml @@ -1,3 +1 @@ - - + diff --git a/plugins/aves_screen_state/pubspec.lock b/plugins/aves_screen_state/pubspec.lock index 10b974b12..d5b51cbb3 100644 --- a/plugins/aves_screen_state/pubspec.lock +++ b/plugins/aves_screen_state/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" collection: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.1" flutter: dependency: "direct main" description: flutter @@ -34,18 +34,18 @@ packages: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.7" lints: dependency: transitive description: name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + sha256: "6b0206b0bf4f04961fc5438198ccb3a885685cd67d4d4a32cc20ad7f8adbe015" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.0" material_color_utilities: dependency: transitive description: @@ -58,10 +58,10 @@ packages: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" plugin_platform_interface: dependency: "direct main" description: @@ -84,4 +84,4 @@ packages: source: hosted version: "2.1.4" sdks: - dart: ">=2.19.6 <3.0.0" + dart: ">=3.0.0 <4.0.0" diff --git a/plugins/aves_screen_state/pubspec.yaml b/plugins/aves_screen_state/pubspec.yaml index 16d083441..9b6570b39 100644 --- a/plugins/aves_screen_state/pubspec.yaml +++ b/plugins/aves_screen_state/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=2.19.6 <4.0.0" + sdk: ">=3.0.0 <4.0.0" dependencies: flutter: diff --git a/plugins/aves_services/pubspec.lock b/plugins/aves_services/pubspec.lock index c1769f099..6332d2e3f 100644 --- a/plugins/aves_services/pubspec.lock +++ b/plugins/aves_services/pubspec.lock @@ -27,10 +27,10 @@ packages: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" clock: dependency: transitive description: @@ -43,10 +43,10 @@ packages: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.1" custom_rounded_rectangle_border: dependency: transitive description: @@ -120,10 +120,10 @@ packages: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.7" latlong2: dependency: "direct main" description: @@ -136,10 +136,10 @@ packages: dependency: transitive description: name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + sha256: "6b0206b0bf4f04961fc5438198ccb3a885685cd67d4d4a32cc20ad7f8adbe015" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.0" lists: dependency: transitive description: @@ -160,10 +160,10 @@ packages: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" mgrs_dart: dependency: transitive description: @@ -253,10 +253,10 @@ packages: dependency: transitive description: name: typed_data - sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" unicode: dependency: transitive description: @@ -282,5 +282,5 @@ packages: source: hosted version: "2.0.0" sdks: - dart: ">=2.19.6 <3.0.0" + dart: ">=3.0.0 <4.0.0" flutter: ">=3.3.0" diff --git a/plugins/aves_services/pubspec.yaml b/plugins/aves_services/pubspec.yaml index 02ad742fe..b19e726a8 100644 --- a/plugins/aves_services/pubspec.yaml +++ b/plugins/aves_services/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=2.19.6 <4.0.0" + sdk: ">=3.0.0 <4.0.0" dependencies: flutter: diff --git a/plugins/aves_services_google/lib/src/map.dart b/plugins/aves_services_google/lib/src/map.dart index b3f50548d..d80565c08 100644 --- a/plugins/aves_services_google/lib/src/map.dart +++ b/plugins/aves_services_google/lib/src/map.dart @@ -118,7 +118,6 @@ class _EntryGoogleMapState extends State> with WidgetsBindi // workaround for blank map when resuming app // cf https://github.com/flutter/flutter/issues/40284 _serviceMapController?.setMapStyle(null); - break; } } diff --git a/plugins/aves_services_google/pubspec.lock b/plugins/aves_services_google/pubspec.lock index 5e08d6ef0..181f9a0a9 100644 --- a/plugins/aves_services_google/pubspec.lock +++ b/plugins/aves_services_google/pubspec.lock @@ -34,10 +34,10 @@ packages: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" clock: dependency: transitive description: @@ -50,10 +50,10 @@ packages: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.1" custom_rounded_rectangle_border: dependency: transitive description: @@ -66,10 +66,10 @@ packages: dependency: "direct main" description: name: device_info_plus - sha256: f52ab3b76b36ede4d135aab80194df8925b553686f0fa12226b4e2d658e45903 + sha256: "9b1a0c32b2a503f8fe9f8764fac7b5fcd4f6bd35d8f49de5350bccf9e2a33b8a" url: "https://pub.dev" source: hosted - version: "8.2.2" + version: "9.0.0" device_info_plus_platform_interface: dependency: transitive description: @@ -90,10 +90,10 @@ packages: dependency: transitive description: name: ffi - sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978 + sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99 url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" file: dependency: transitive description: @@ -180,10 +180,10 @@ packages: dependency: "direct main" description: name: google_maps_flutter_android - sha256: b1aeab571b33e983ced9977f2f0eca2b25925f6319e99e3901cfc1675e1b5ada + sha256: "640419bb8bf1353c6698c5ca0eb3773e58b08e820eb111e84a648e6de2b3f4a0" url: "https://pub.dev" source: hosted - version: "2.4.13" + version: "2.4.14" google_maps_flutter_ios: dependency: transitive description: @@ -228,10 +228,10 @@ packages: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.7" latlong2: dependency: "direct main" description: @@ -244,10 +244,10 @@ packages: dependency: transitive description: name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + sha256: "6b0206b0bf4f04961fc5438198ccb3a885685cd67d4d4a32cc20ad7f8adbe015" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.0" lists: dependency: transitive description: @@ -268,10 +268,10 @@ packages: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" mgrs_dart: dependency: transitive description: @@ -377,10 +377,10 @@ packages: dependency: transitive description: name: typed_data - sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" unicode: dependency: transitive description: @@ -405,6 +405,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.4" + win32_registry: + dependency: transitive + description: + name: win32_registry + sha256: "1c52f994bdccb77103a6231ad4ea331a244dbcef5d1f37d8462f713143b0bfae" + url: "https://pub.dev" + source: hosted + version: "1.1.0" wkt_parser: dependency: transitive description: @@ -414,5 +422,5 @@ packages: source: hosted version: "2.0.0" sdks: - dart: ">=2.19.6 <3.0.0" + dart: ">=3.0.0 <4.0.0" flutter: ">=3.3.0" diff --git a/plugins/aves_services_google/pubspec.yaml b/plugins/aves_services_google/pubspec.yaml index dc926c15b..c072898a4 100644 --- a/plugins/aves_services_google/pubspec.yaml +++ b/plugins/aves_services_google/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=2.19.6 <4.0.0" + sdk: ">=3.0.0 <4.0.0" dependencies: flutter: diff --git a/plugins/aves_services_huawei/pubspec.lock b/plugins/aves_services_huawei/pubspec.lock index 338b36941..0eda3f593 100644 --- a/plugins/aves_services_huawei/pubspec.lock +++ b/plugins/aves_services_huawei/pubspec.lock @@ -41,10 +41,10 @@ packages: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" clock: dependency: transitive description: @@ -57,10 +57,10 @@ packages: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.1" custom_rounded_rectangle_border: dependency: transitive description: @@ -150,10 +150,10 @@ packages: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.7" latlong2: dependency: "direct main" description: @@ -166,10 +166,10 @@ packages: dependency: transitive description: name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + sha256: "6b0206b0bf4f04961fc5438198ccb3a885685cd67d4d4a32cc20ad7f8adbe015" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.0" lists: dependency: transitive description: @@ -190,10 +190,10 @@ packages: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" mgrs_dart: dependency: transitive description: @@ -291,10 +291,10 @@ packages: dependency: transitive description: name: typed_data - sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" unicode: dependency: transitive description: @@ -320,5 +320,5 @@ packages: source: hosted version: "2.0.0" sdks: - dart: ">=2.19.6 <3.0.0" + dart: ">=3.0.0 <4.0.0" flutter: ">=3.3.0" diff --git a/plugins/aves_services_huawei/pubspec.yaml b/plugins/aves_services_huawei/pubspec.yaml index 8043804ce..45c94edd3 100644 --- a/plugins/aves_services_huawei/pubspec.yaml +++ b/plugins/aves_services_huawei/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=2.19.6 <4.0.0" + sdk: ">=3.0.0 <4.0.0" dependencies: flutter: diff --git a/plugins/aves_services_none/pubspec.lock b/plugins/aves_services_none/pubspec.lock index a67767953..5ccd3af10 100644 --- a/plugins/aves_services_none/pubspec.lock +++ b/plugins/aves_services_none/pubspec.lock @@ -34,10 +34,10 @@ packages: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" clock: dependency: transitive description: @@ -50,10 +50,10 @@ packages: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.1" custom_rounded_rectangle_border: dependency: transitive description: @@ -127,10 +127,10 @@ packages: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.7" latlong2: dependency: "direct main" description: @@ -143,10 +143,10 @@ packages: dependency: transitive description: name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + sha256: "6b0206b0bf4f04961fc5438198ccb3a885685cd67d4d4a32cc20ad7f8adbe015" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.0" lists: dependency: transitive description: @@ -167,10 +167,10 @@ packages: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" mgrs_dart: dependency: transitive description: @@ -260,10 +260,10 @@ packages: dependency: transitive description: name: typed_data - sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" unicode: dependency: transitive description: @@ -289,5 +289,5 @@ packages: source: hosted version: "2.0.0" sdks: - dart: ">=2.19.6 <3.0.0" + dart: ">=3.0.0 <4.0.0" flutter: ">=3.3.0" diff --git a/plugins/aves_services_none/pubspec.yaml b/plugins/aves_services_none/pubspec.yaml index 8ef4e9ad0..c189e084e 100644 --- a/plugins/aves_services_none/pubspec.yaml +++ b/plugins/aves_services_none/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=2.19.6 <4.0.0" + sdk: ">=3.0.0 <4.0.0" dependencies: flutter: diff --git a/plugins/aves_ui/pubspec.lock b/plugins/aves_ui/pubspec.lock index c656d7419..65a37f405 100644 --- a/plugins/aves_ui/pubspec.lock +++ b/plugins/aves_ui/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" collection: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.1" flutter: dependency: "direct main" description: flutter @@ -34,18 +34,18 @@ packages: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.7" lints: dependency: transitive description: name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + sha256: "6b0206b0bf4f04961fc5438198ccb3a885685cd67d4d4a32cc20ad7f8adbe015" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.0" material_color_utilities: dependency: transitive description: @@ -58,10 +58,10 @@ packages: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" sky_engine: dependency: transitive description: flutter @@ -76,4 +76,4 @@ packages: source: hosted version: "2.1.4" sdks: - dart: ">=2.19.6 <3.0.0" + dart: ">=3.0.0 <4.0.0" diff --git a/plugins/aves_ui/pubspec.yaml b/plugins/aves_ui/pubspec.yaml index 526345b23..bc1256456 100644 --- a/plugins/aves_ui/pubspec.yaml +++ b/plugins/aves_ui/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=2.19.6 <4.0.0" + sdk: ">=3.0.0 <4.0.0" dependencies: flutter: diff --git a/plugins/aves_utils/pubspec.lock b/plugins/aves_utils/pubspec.lock index c656d7419..65a37f405 100644 --- a/plugins/aves_utils/pubspec.lock +++ b/plugins/aves_utils/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" collection: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.1" flutter: dependency: "direct main" description: flutter @@ -34,18 +34,18 @@ packages: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.7" lints: dependency: transitive description: name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + sha256: "6b0206b0bf4f04961fc5438198ccb3a885685cd67d4d4a32cc20ad7f8adbe015" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.0" material_color_utilities: dependency: transitive description: @@ -58,10 +58,10 @@ packages: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" sky_engine: dependency: transitive description: flutter @@ -76,4 +76,4 @@ packages: source: hosted version: "2.1.4" sdks: - dart: ">=2.19.6 <3.0.0" + dart: ">=3.0.0 <4.0.0" diff --git a/plugins/aves_utils/pubspec.yaml b/plugins/aves_utils/pubspec.yaml index 65152ff34..dd19f728c 100644 --- a/plugins/aves_utils/pubspec.yaml +++ b/plugins/aves_utils/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=2.19.6 <4.0.0" + sdk: ">=3.0.0 <4.0.0" dependencies: flutter: diff --git a/plugins/aves_video/pubspec.lock b/plugins/aves_video/pubspec.lock index 35cc0e9c3..bde974666 100644 --- a/plugins/aves_video/pubspec.lock +++ b/plugins/aves_video/pubspec.lock @@ -12,18 +12,18 @@ packages: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" collection: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.1" equatable: dependency: transitive description: @@ -49,18 +49,18 @@ packages: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.7" lints: dependency: transitive description: name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + sha256: "6b0206b0bf4f04961fc5438198ccb3a885685cd67d4d4a32cc20ad7f8adbe015" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.0" material_color_utilities: dependency: transitive description: @@ -73,10 +73,10 @@ packages: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" sky_engine: dependency: transitive description: flutter @@ -91,4 +91,4 @@ packages: source: hosted version: "2.1.4" sdks: - dart: ">=2.19.6 <3.0.0" + dart: ">=3.0.0 <4.0.0" diff --git a/plugins/aves_video/pubspec.yaml b/plugins/aves_video/pubspec.yaml index 51a036afd..7c477db6e 100644 --- a/plugins/aves_video/pubspec.yaml +++ b/plugins/aves_video/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=2.19.6 <4.0.0" + sdk: ">=3.0.0 <4.0.0" dependencies: flutter: diff --git a/pubspec.lock b/pubspec.lock index 4b4eea797..b2210d693 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,34 +5,34 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "8880b4cfe7b5b17d57c052a5a3a8cc1d4f546261c7cc8fbd717bd53f48db0568" + sha256: "405666cd3cf0ee0a48d21ec67e65406aad2c726d9fa58840d3375e7bdcd32a07" url: "https://pub.dev" source: hosted - version: "59.0.0" + version: "60.0.0" _flutterfire_internals: dependency: transitive description: name: _flutterfire_internals - sha256: "867b77e2367bc502dcd4d5a66302615409f04eb20ed82ba1c0ba073f9107e018" + sha256: d687576bb973e8d2539d0b4615d985336269a0dbe1b3984e937fd22d31406fb8 url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.0" analyzer: dependency: transitive description: name: analyzer - sha256: a89627f49b0e70e068130a36571409726b04dab12da7e5625941d2c8ec278b96 + sha256: "1952250bd005bacb895a01bf1b4dc00e3ba1c526cf47dca54dfe24979c65f5b3" url: "https://pub.dev" source: hosted - version: "5.11.1" + version: "5.12.0" archive: dependency: transitive description: name: archive - sha256: "80e5141fafcb3361653ce308776cfd7d45e6e9fbb429e14eec571382c0c5fecb" + sha256: "0c8368c9b3f0abbc193b9d6133649a614204b528982bebc7026372d61677ce3a" url: "https://pub.dev" source: hosted - version: "3.3.2" + version: "3.3.7" args: dependency: transitive description: @@ -45,10 +45,10 @@ packages: dependency: transitive description: name: async - sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.11.0" aves_magnifier: dependency: "direct main" description: @@ -154,25 +154,26 @@ packages: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" charts_common: dependency: transitive description: - name: charts_common - sha256: "7b8922f9b0d9b134122756a787dab1c3946ae4f3fc5022ff323ba0014998ea02" - url: "https://pub.dev" - source: hosted + path: charts_common + ref: aves + resolved-ref: "6c44dc05289c1a5dbf0b027dbba083669843072c" + url: "https://github.com/deckerst/flutter_google_charts.git" + source: git version: "0.12.0" charts_flutter: dependency: "direct main" description: path: charts_flutter - ref: master - resolved-ref: de76a46c1908b0c35aaf60823100fe9bfa26ae4d - url: "https://github.com/fzyzcjy/charts.git" + ref: aves + resolved-ref: "6c44dc05289c1a5dbf0b027dbba083669843072c" + url: "https://github.com/deckerst/flutter_google_charts.git" source: git version: "0.12.0" clock: @@ -187,18 +188,18 @@ packages: dependency: "direct main" description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.1" connectivity_plus: dependency: "direct main" description: name: connectivity_plus - sha256: b74247fad72c171381dbe700ca17da24deac637ab6d43c343b42867acb95c991 + sha256: "45262924896ff72a8cd92b722bb7e3d5020f9e0724531a3e10e22ddae2005991" url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "4.0.0" connectivity_plus_platform_interface: dependency: transitive description: @@ -235,10 +236,10 @@ packages: dependency: transitive description: name: crypto - sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.3" custom_rounded_rectangle_border: dependency: transitive description: @@ -266,11 +267,12 @@ packages: device_info_plus: dependency: "direct main" description: - name: device_info_plus - sha256: f52ab3b76b36ede4d135aab80194df8925b553686f0fa12226b4e2d658e45903 - url: "https://pub.dev" - source: hosted - version: "8.2.2" + path: "packages/device_info_plus/device_info_plus" + ref: main + resolved-ref: e8d3b445ce52012456126a3844ddb49b92c5c850 + url: "https://github.com/fluttercommunity/plus_plugins.git" + source: git + version: "9.0.0" device_info_plus_platform_interface: dependency: transitive description: @@ -283,10 +285,10 @@ packages: dependency: "direct main" description: name: dynamic_color - sha256: bbebb1b7ebed819e0ec83d4abdc2a8482d934f6a85289ffc1c6acf7589fa2aad + sha256: "1ffc93e3794696af22ff11d75adcd6f5fb67b45bd54f9d8735a5d1cf8c013841" url: "https://pub.dev" source: hosted - version: "1.6.3" + version: "1.6.4" equatable: dependency: "direct main" description: @@ -324,16 +326,16 @@ packages: dependency: transitive description: name: ffi - sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978 + sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99 url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" fijkplayer: dependency: "direct main" description: path: "." ref: aves - resolved-ref: aebc9d7f14c8226885b6e02d7955ba6a696d454b + resolved-ref: "935a2d86ebf45fbdbaf8b4a0921d5eaea87410d6" url: "https://github.com/deckerst/fijkplayer.git" source: git version: "0.10.0" @@ -349,65 +351,66 @@ packages: dependency: transitive description: name: firebase_core - sha256: dcf54c170c5371ad0e79229d0fb372c58262ae0968b7de222bf28e51dd236be0 + sha256: "4491238f4fddc885bc994e304a035eb8aba2c935816b2c0b31d87f3ec6e96682" url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.12.0" firebase_core_platform_interface: dependency: transitive description: name: firebase_core_platform_interface - sha256: ae79f335f6c7f2dadb00c98c429da2ca905d265e0225fb5e7dfa62ac3accad48 + sha256: b63e3be6c96ef5c33bdec1aab23c91eb00696f6452f0519401d640938c94cba2 url: "https://pub.dev" source: hosted - version: "4.7.0" + version: "4.8.0" firebase_core_web: dependency: transitive description: name: firebase_core_web - sha256: e57ef862257a0d977c1308d02e2fbb9b68525e6d85711b08f3df8cec836fb444 + sha256: "8c0f4c87d20e2d001a5915df238c1f9c88704231f591324205f5a5d2a7740a45" url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.5.0" firebase_crashlytics: dependency: transitive description: name: firebase_crashlytics - sha256: e8424f75c0fd466a6ae4d069b313ec84133ef275c00a5a3a416b75b99fb3e831 + sha256: cc6cab05d4de3257408c167ec1e2e410f21c0764aea22f78481314496a1cc4ca url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "3.3.0" firebase_crashlytics_platform_interface: dependency: transitive description: name: firebase_crashlytics_platform_interface - sha256: "381e28f33d5255a698ac7755c3c4fdff42382be0154f375564e6852aa46f9d8f" + sha256: b3b6017045866b981b566896a0de6114181bf9e91465221f2e89996f47a160b4 url: "https://pub.dev" source: hosted - version: "3.5.0" + version: "3.6.0" flex_color_picker: dependency: "direct main" description: name: flex_color_picker - sha256: fc035dbef0975dd2650f9db1335c552e3b0ce87da4900b00f6b98cd6c78cbe42 + sha256: d8279250820ad279123fa8ee94151dd99400dd9ef4fb096589fcf956765d39a9 url: "https://pub.dev" source: hosted - version: "3.2.1" + version: "3.2.2" flex_seed_scheme: dependency: transitive description: name: flex_seed_scheme - sha256: b3678d82403c13dec2ee2721e078b26f14577712411b6aa981b0f4269df3fabb + sha256: e4168a6fc88a3e5bc3d6b7a748c6a6083eedc193d343ddc26bbad7fb1b258555 url: "https://pub.dev" source: hosted - version: "1.2.4" + version: "1.3.0" floating: dependency: "direct main" description: - name: floating - sha256: c6380d9cbf628370c68749fa1f0675bc5b69d9a5b923b84660e79b3e6c9a47ec - url: "https://pub.dev" - source: hosted + path: "." + ref: kotlin-jvm-target + resolved-ref: "4781520e090c3a104e2478cfe8e8cb97235341ec" + url: "https://github.com/deckerst/floating.git" + source: git version: "2.0.0" fluster: dependency: "direct main" @@ -523,10 +526,10 @@ packages: dependency: "direct main" description: name: get_it - sha256: "43133b45f32f1d96bbaeb43ea35a50ce854981baa80f47c3e26ee2ad23bef113" + sha256: "529de303c739fca98cd7ece5fca500d8ff89649f1bb4b4e94fb20954abcd7468" url: "https://pub.dev" source: hosted - version: "7.5.0" + version: "7.6.0" glob: dependency: transitive description: @@ -544,12 +547,13 @@ packages: source: hosted version: "5.0.0" google_api_availability_android: - dependency: transitive + dependency: "direct overridden" description: - name: google_api_availability_android - sha256: eb309bc0b435731d18f306b598e176a9afcf642089a7d7c5cbb48e393afda345 - url: "https://pub.dev" - source: hosted + path: google_api_availability_android + ref: agp8-compat + resolved-ref: d227c28fed8dff3bc6afb1d09248cf0ab8824473 + url: "https://github.com/deckerst/flutter-google-api-availability.git" + source: git version: "1.0.0+1" google_api_availability_platform_interface: dependency: transitive @@ -571,10 +575,10 @@ packages: dependency: transitive description: name: google_maps_flutter_android - sha256: b1aeab571b33e983ced9977f2f0eca2b25925f6319e99e3901cfc1675e1b5ada + sha256: "640419bb8bf1353c6698c5ca0eb3773e58b08e820eb111e84a648e6de2b3f4a0" url: "https://pub.dev" source: hosted - version: "2.4.13" + version: "2.4.14" google_maps_flutter_ios: dependency: transitive description: @@ -635,10 +639,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91" + sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6 url: "https://pub.dev" source: hosted - version: "0.17.0" + version: "0.18.0" io: dependency: transitive description: @@ -651,10 +655,10 @@ packages: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.7" latlong2: dependency: "direct main" description: @@ -667,10 +671,10 @@ packages: dependency: transitive description: name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + sha256: "6b0206b0bf4f04961fc5438198ccb3a885685cd67d4d4a32cc20ad7f8adbe015" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.0" lists: dependency: transitive description: @@ -699,10 +703,10 @@ packages: dependency: transitive description: name: local_auth_ios - sha256: "604078f6492fe7730fc5bb8e4f2cfe2bc287a9b499ea0ff30a29925fc1873728" + sha256: "503a938c4edde6b244c6ee3b1e2e675ddb7e37e79d5056658dbed1997cf04785" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" local_auth_platform_interface: dependency: transitive description: @@ -739,18 +743,18 @@ packages: dependency: transitive description: name: matcher - sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" url: "https://pub.dev" source: hosted - version: "0.12.13" + version: "0.12.15" material_color_utilities: dependency: "direct main" description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.5.0" material_design_icons_flutter: dependency: "direct main" description: @@ -763,10 +767,10 @@ packages: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" mgrs_dart: dependency: transitive description: @@ -788,7 +792,7 @@ packages: description: path: "." ref: aves - resolved-ref: f05bca5ca9d74c65ae966f6e6f88c511ceedbef8 + resolved-ref: "45e13ba987b00261d00488d29fa6f97ed0bcb96a" url: "https://github.com/deckerst/aves_panorama_motion_sensors.git" source: git version: "0.1.0" @@ -835,11 +839,12 @@ packages: package_info_plus: dependency: "direct main" description: - name: package_info_plus - sha256: "10259b111176fba5c505b102e3a5b022b51dd97e30522e906d6922c745584745" - url: "https://pub.dev" - source: hosted - version: "3.1.2" + path: "packages/package_info_plus/package_info_plus" + ref: main + resolved-ref: e8d3b445ce52012456126a3844ddb49b92c5c850 + url: "https://github.com/fluttercommunity/plus_plugins.git" + source: git + version: "4.0.0" package_info_plus_platform_interface: dependency: transitive description: @@ -869,10 +874,10 @@ packages: dependency: "direct main" description: name: path - sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" url: "https://pub.dev" source: hosted - version: "1.8.2" + version: "1.8.3" path_parsing: dependency: transitive description: @@ -917,10 +922,18 @@ packages: dependency: "direct main" description: name: pdf - sha256: "586d3debf5432e5377044754032cfa53ab45e9abf371d4865e9ad5019570e246" + sha256: "70d84154dc5b6ddf28eee6c012510a4cbbebb3a1879c0957e05364a95e8f3832" url: "https://pub.dev" source: hosted - version: "3.10.1" + version: "3.10.3" + pdf_widget_wrapper: + dependency: transitive + description: + name: pdf_widget_wrapper + sha256: e9d31fd7782ce28ae346b127ea7d1cd748d799bddee379f31191693610e23749 + url: "https://pub.dev" + source: hosted + version: "1.0.1" percent_indicator: dependency: "direct main" description: @@ -938,12 +951,13 @@ packages: source: hosted version: "10.2.0" permission_handler_android: - dependency: transitive + dependency: "direct overridden" description: - name: permission_handler_android - sha256: "8028362b40c4a45298f1cbfccd227c8dd6caf0e27088a69f2ba2ab15464159e2" - url: "https://pub.dev" - source: hosted + path: permission_handler_android + ref: agp8-compat + resolved-ref: ad409917f0db1b1ad6a2c74d3c6b3b35f331a81e + url: "https://github.com/deckerst/flutter-permission-handler.git" + source: git version: "10.2.0" permission_handler_apple: dependency: transitive @@ -973,10 +987,10 @@ packages: dependency: transitive description: name: petitparser - sha256: "49392a45ced973e8d94a85fdb21293fbb40ba805fc49f2965101ae748a3683b4" + sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 url: "https://pub.dev" source: hosted - version: "5.1.0" + version: "5.4.0" pin_code_fields: dependency: "direct main" description: @@ -1001,6 +1015,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + pointycastle: + dependency: transitive + description: + name: pointycastle + sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c" + url: "https://pub.dev" + source: hosted + version: "3.7.3" polylabel: dependency: transitive description: @@ -1021,10 +1043,10 @@ packages: dependency: "direct main" description: name: printing - sha256: c5c19dd852e95aa140141df13fa304f079a20c4a14a66de5275a0f811240aeec + sha256: "6aa86779d51f1c60608defee7b231e1133ab9b00f63b3b71abfa85cb39898571" url: "https://pub.dev" source: hosted - version: "5.10.3" + version: "5.10.4" process: dependency: transitive description: @@ -1077,10 +1099,10 @@ packages: dependency: transitive description: name: screen_brightness_android - sha256: "69231ea2cf83a627120302a82e98e739ee7e97c1077b58fd0ff0ad954e95a36e" + sha256: "3df10961e3a9e968a5e076fe27e7f4741fa8a1d3950bdeb48cf121ed529d0caf" url: "https://pub.dev" source: hosted - version: "0.1.0+1" + version: "0.1.0+2" screen_brightness_ios: dependency: transitive description: @@ -1117,10 +1139,10 @@ packages: dependency: "direct main" description: name: shared_preferences - sha256: "858aaa72d8f61637d64e776aca82e1c67e6d9ee07979123c5d17115031c1b13b" + sha256: "16d3fb6b3692ad244a695c0183fca18cf81fd4b821664394a781de42386bf022" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" shared_preferences_android: dependency: transitive description: @@ -1133,10 +1155,10 @@ packages: dependency: transitive description: name: shared_preferences_foundation - sha256: "0c1c16c56c9708aa9c361541a6f0e5cc6fc12a3232d866a687a7b7db30032b07" + sha256: e014107bb79d6d3297196f4f2d0db54b5d1f85b8ea8ff63b8e8b391a02700feb url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.2.2" shared_preferences_linux: dependency: transitive description: @@ -1242,10 +1264,10 @@ packages: dependency: "direct main" description: name: sqflite - sha256: acf091c6e55c50d00b30b8532b2dd23e393cf775861665ebd0f15cdd6ebfb079 + sha256: "3a82c9a216b46b88617e3714dd74227eaca20c501c4abcc213e56db26b9caa00" url: "https://pub.dev" source: hosted - version: "2.2.8+1" + version: "2.2.8+2" sqflite_common: dependency: transitive description: @@ -1283,7 +1305,7 @@ packages: description: path: "." ref: HEAD - resolved-ref: "3c3471b548b3e8a2fbeefb9eb761f442b5cc9fbf" + resolved-ref: "096f964597703830f384d1a336e7752a6531f2a2" url: "https://github.com/deckerst/aves_streams_channel.git" source: git version: "0.3.0" @@ -1323,26 +1345,26 @@ packages: dependency: "direct dev" description: name: test - sha256: a5fcd2d25eeadbb6589e80198a47d6a464ba3e2049da473943b8af9797900c2d + sha256: "3dac9aecf2c3991d09b9cdde4f98ded7b30804a88a0d7e4e7e1678e78d6b97f4" url: "https://pub.dev" source: hosted - version: "1.22.0" + version: "1.24.1" test_api: dependency: transitive description: name: test_api - sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb url: "https://pub.dev" source: hosted - version: "0.4.16" + version: "0.5.1" test_core: dependency: transitive description: name: test_core - sha256: "0ef9755ec6d746951ba0aabe62f874b707690b5ede0fecc818b138fcc9b14888" + sha256: "5138dbffb77b2289ecb12b81c11ba46036590b72a64a7a90d6ffb880f1a29e93" url: "https://pub.dev" source: hosted - version: "0.4.20" + version: "0.5.1" transparent_image: dependency: "direct main" description: @@ -1363,10 +1385,10 @@ packages: dependency: transitive description: name: typed_data - sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" unicode: dependency: transitive description: @@ -1379,18 +1401,18 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: "75f2846facd11168d007529d6cd8fcb2b750186bea046af9711f10b907e1587e" + sha256: eb1e00ab44303d50dd487aab67ebc575456c146c6af44422f9c13889984c00f3 url: "https://pub.dev" source: hosted - version: "6.1.10" + version: "6.1.11" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: "22f8db4a72be26e9e3a4aa3f194b1f7afbc76d20ec141f84be1d787db2155cbd" + sha256: "7aac14be5f4731b923cc697ae2d42043945076cd0dbb8806baecc92c1dc88891" url: "https://pub.dev" source: hosted - version: "6.0.31" + version: "6.0.33" url_launcher_ios: dependency: transitive description: @@ -1451,26 +1473,27 @@ packages: dependency: transitive description: name: vm_service - sha256: e7fb6c2282f7631712b69c19d1bff82f3767eea33a2321c14fa59ad67ea391c7 + sha256: f6deed8ed625c52864792459709183da231ebf66ff0cf09e69b573227c377efe url: "https://pub.dev" source: hosted - version: "9.4.0" + version: "11.3.0" volume_controller: dependency: "direct main" description: - name: volume_controller - sha256: "3bcaadd0e3298286c4ef3b758a3ea44fc98a9abe24d0e7ef087bfab72a4b6924" - url: "https://pub.dev" - source: hosted + path: "." + ref: agp8-compat + resolved-ref: b5eddb8c3501e16814f39873d7fba4a73aa0f080 + url: "https://github.com/deckerst/volume_controller.git" + source: git version: "2.0.6" watcher: dependency: transitive description: name: watcher - sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0" + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.1.0" web_socket_channel: dependency: transitive description: @@ -1483,10 +1506,10 @@ packages: dependency: transitive description: name: webdriver - sha256: ef67178f0cc7e32c1494645b11639dd1335f1d18814aa8435113a92e9ef9d841 + sha256: "3c923e918918feeb90c4c9fdf1fe39220fa4c0e8e2c0fffaded174498ef86c49" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" webkit_inspection_protocol: dependency: transitive description: @@ -1503,6 +1526,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.4" + win32_registry: + dependency: transitive + description: + name: win32_registry + sha256: "1c52f994bdccb77103a6231ad4ea331a244dbcef5d1f37d8462f713143b0bfae" + url: "https://pub.dev" + source: hosted + version: "1.1.0" wkt_parser: dependency: transitive description: @@ -1523,10 +1554,10 @@ packages: dependency: "direct main" description: name: xml - sha256: "979ee37d622dec6365e2efa4d906c37470995871fe9ae080d967e192d88286b5" + sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" url: "https://pub.dev" source: hosted - version: "6.2.2" + version: "6.3.0" yaml: dependency: transitive description: @@ -1536,5 +1567,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=2.19.6 <3.0.0" - flutter: ">=3.7.12" + dart: ">=3.0.0 <4.0.0" + flutter: ">=3.10.0" diff --git a/pubspec.yaml b/pubspec.yaml index e1cf79658..639a13adc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,8 +13,8 @@ publish_to: none environment: # this project bundles Flutter SDK via `flutter_wrapper` # cf https://github.com/passsy/flutter_wrapper - flutter: 3.7.12 - sdk: ">=2.19.6 <4.0.0" + flutter: 3.10.0 + sdk: ">=3.0.0 <4.0.0" # use `scripts/apply_flavor_{flavor}.sh` to set the right dependencies for the flavor dependencies: @@ -46,8 +46,11 @@ dependencies: path: plugins/aves_utils charts_flutter: git: - url: https://github.com/fzyzcjy/charts.git - ref: master + #url: https://github.com/fzyzcjy/charts.git + #ref: master + url: https://github.com/deckerst/flutter_google_charts.git + # fzyzcjy's master commit de76a46 (Sep 26, 2022) is incompatible with Flutter v3.10 + ref: aves path: charts_flutter collection: connectivity_plus: @@ -66,6 +69,12 @@ dependencies: ref: aves flex_color_picker: floating: + git: + # url: https://github.com/wrbl606/floating.git + # # v2.0.0 is incompatible with AGP8 + # ref: main + url: https://github.com/deckerst/floating.git + ref: kotlin-jvm-target fluster: flutter_displaymode: flutter_highlight: @@ -116,6 +125,41 @@ dev_dependencies: shared_preferences_platform_interface: test: +dependency_overrides: + # as of Flutter beta v3.10.0-1.5.pre, `flutter_driver` + # constrains `material_color_utilities` to v0.2.0, which + # constrains `dynamic_color` to v1.6.4, which is incompatible with AGP8 + material_color_utilities: ^0.5.0 + device_info_plus: + git: + url: https://github.com/fluttercommunity/plus_plugins.git + # v9:0:0 is incompatible with AGP8 + ref: main + path: packages/device_info_plus/device_info_plus + package_info_plus: + git: + url: https://github.com/fluttercommunity/plus_plugins.git + # v4:0:0 is incompatible with AGP8 + ref: main + path: packages/package_info_plus/package_info_plus + # cf https://github.com/Baseflow/flutter-google-api-availability/pull/43 + google_api_availability_android: + git: + url: https://github.com/deckerst/flutter-google-api-availability.git + ref: agp8-compat + path: google_api_availability_android + # cf https://github.com/Baseflow/flutter-permission-handler/pull/1039 + permission_handler_android: + git: + url: https://github.com/deckerst/flutter-permission-handler.git + ref: agp8-compat + path: permission_handler_android + # cf https://github.com/kurenai7968/volume_controller/pull/19 + volume_controller: + git: + url: https://github.com/deckerst/volume_controller.git + ref: agp8-compat + flutter: assets: - assets/ @@ -155,27 +199,27 @@ flutter: # adapts from package `flutter_highlight` v0.7.0 # # `OutputBuffer` in `/services/common/output_buffer.dart` -# adapts from Flutter v3.3.3 `_OutputBuffer` in `/foundation/consolidate_response.dart` +# adapts from Flutter v3.10.0 `_OutputBuffer` in `/foundation/consolidate_response.dart` # # `TvLicensePage` in `/widgets/about/tv_license_page.dart` -# adapts from Flutter v3.7.7 `_LicenseData` in `/material/about.dart` +# adapts from Flutter v3.10.0 `_LicenseData` in `/material/about.dart` # and `_PackageLicensePage` in `/material/about.dart` # # `OverlaySnackBar` in `/widgets/common/action_mixins/overlay_snack_bar.dart` -# adapts from Flutter v3.3.3 `SnackBar` in `/material/snack_bar.dart` +# adapts from Flutter v3.10.0 `SnackBar` in `/material/snack_bar.dart` # # `EagerScaleGestureRecognizer` in `/widgets/common/behaviour/eager_scale_gesture_recognizer.dart` -# adapts from Flutter v3.3.3 `ScaleGestureRecognizer` in `/gestures/scale.dart` +# adapts from Flutter v3.10.0 `ScaleGestureRecognizer` in `/gestures/scale.dart` # # `KnownExtentScrollPhysics` in `/widgets/common/behaviour/known_extent_scroll_physics.dart` -# adapts from Flutter v3.3.3 `FixedExtentScrollPhysics` in `/widgets/list_wheel_scroll_view.dart` +# adapts from Flutter v3.10.0 `FixedExtentScrollPhysics` in `/widgets/list_wheel_scroll_view.dart` # # `TransitionImage` in `/widgets/common/fx/transition_image.dart` -# adapts from Flutter v3.3.3 `_ImageState` in `/widgets/image.dart` +# adapts from Flutter v3.10.0 `_ImageState` in `/widgets/image.dart` # and `DecorationImagePainter` in `/painting/decoration_image.dart` # # `_RenderSliverKnownExtentBoxAdaptor` in `/widgets/common/grid/sliver.dart` -# adapts from Flutter v3.3.3 `RenderSliverFixedExtentBoxAdaptor` in `/rendering/sliver_fixed_extent_list.dart` +# adapts from Flutter v3.10.0 `RenderSliverFixedExtentBoxAdaptor` in `/rendering/sliver_fixed_extent_list.dart` # # `CollectionSearchDelegate`, `SearchPageRoute` in `/widgets/search/search_delegate.dart` -# adapts from Flutter v3.3.3 `SearchDelegate`, `_SearchPageRoute` in `/material/search.dart` +# adapts from Flutter v3.10.0 `SearchDelegate`, `_SearchPageRoute` in `/material/search.dart` diff --git a/shaders.sksl.json b/shaders.sksl.json index 3d907597d..5e5087ff8 100644 --- a/shaders.sksl.json +++ b/shaders.sksl.json @@ -1 +1 @@ -{"platform":"android","name":"SM G970N","engineRevision":"1a65d409c7a1438a34d21b60bf30a6fd5db59314","data":{"DASAAAAAQAAWAABAYAAQBYH7777Z6QQBAEAAAAAAEAAAAAAAEBSAAAB2AAAAAAACAAAAAEBSAAAAA":"CgAAAExTS1PVAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQyIHVBdGxhc1NpemVJbnZfUzA7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiB1c2hvcnQyIGluVGV4dHVyZUNvb3JkczsKb3V0IGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKZmxhdCBvdXQgZmxvYXQgdlRleEluZGV4X1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBCaXRtYXBUZXh0CglpbnQgdGV4SWR4ID0gMDsKCWZsb2F0MiB1bm9ybVRleENvb3JkcyA9IGZsb2F0MihpblRleHR1cmVDb29yZHMueCwgaW5UZXh0dXJlQ29vcmRzLnkpOwoJdlRleHR1cmVDb29yZHNfUzAgPSB1bm9ybVRleENvb3JkcyAqIHVBdGxhc1NpemVJbnZfUzA7Cgl2VGV4SW5kZXhfUzAgPSBmbG9hdCh0ZXhJZHgpOwoJZmxvYXQyIF90bXBfMV9pblBvc2l0aW9uID0gaW5Qb3NpdGlvbjsKCXNrX1Bvc2l0aW9uID0gaW5Qb3NpdGlvbi54eTAxOwp9CgAAAAAAAADgAQAAdW5pZm9ybSBoYWxmNCB1Q29sb3JfUzA7CnVuaWZvcm0gc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MwOwppbiBmbG9hdDIgdlRleHR1cmVDb29yZHNfUzA7CmZsYXQgaW4gZmxvYXQgdlRleEluZGV4X1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgQml0bWFwVGV4dAoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHVDb2xvcl9TMDsKCWhhbGY0IHRleENvbG9yOwoJewoJCXRleENvbG9yID0gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB2VGV4dHVyZUNvb3Jkc19TMCk7Cgl9CglvdXRwdXRDb2xvcl9TMCA9IG91dHB1dENvbG9yX1MwICogdGV4Q29sb3I7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAKAAAAaW5Qb3NpdGlvbgAADwAAAGluVGV4dHVyZUNvb3JkcwAAAAAA","HUIAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAAAAAALUAQMAAAAAAABAEAAAABJYQAAAAAACAIAAAAAWCBAAAIBAAAAANAECAZAAAAQAAAAAAFAAMAAAABAAAAAAABBAM":"CgAAAExTS1M2AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAAAAC8GAAB1bmlmb3JtIGZsb2F0NCB1Y2xhbXBfUzFfYzBfYzBfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMDsKdW5pZm9ybSBoYWxmMiB1SW5jcmVtZW50X1MxX2MwOwp1bmlmb3JtIGhhbGYyIHVPZmZzZXRzQW5kS2VybmVsX1MxX2MwWzEzXTsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxOwp1bmlmb3JtIHNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMTsKaW4gZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwpoYWxmNCBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwX2MwKGhhbGY0IF9pbnB1dCwgZmxvYXQyIF9jb29yZHMpIAp7CglmbG9hdDIgaW5Db29yZCA9IF9jb29yZHM7CglmbG9hdDIgc3Vic2V0Q29vcmQ7CglzdWJzZXRDb29yZC54ID0gaW5Db29yZC54OwoJc3Vic2V0Q29vcmQueSA9IGluQ29vcmQueTsKCWZsb2F0MiBjbGFtcGVkQ29vcmQ7CgljbGFtcGVkQ29vcmQueCA9IHN1YnNldENvb3JkLng7CgljbGFtcGVkQ29vcmQueSA9IGNsYW1wKHN1YnNldENvb3JkLnksIHVjbGFtcF9TMV9jMF9jMF9jMC55LCB1Y2xhbXBfUzFfYzBfYzBfYzAudyk7CgloYWxmNCB0ZXh0dXJlQ29sb3IgPSBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzEsIGNsYW1wZWRDb29yZCk7CglyZXR1cm4gdGV4dHVyZUNvbG9yOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMV9jMF9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBfY29vcmRzKSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzBfYzBfYzAoX2lucHV0LCBmbG9hdDN4Mih1bWF0cml4X1MxX2MwX2MwKSAqIF9jb29yZHMueHkxKTsKfQpoYWxmNCBTbW9vdGhfUzFfYzAoaGFsZjQgX2lucHV0LCBmbG9hdDIgY29vcmQsIGhhbGYyIG9mZnNldEFuZEtlcm5lbCkgCnsKCXJldHVybiBNYXRyaXhFZmZlY3RfUzFfYzBfYzAoX2lucHV0LCAoY29vcmQgKyBvZmZzZXRBbmRLZXJuZWwueCAqIHVJbmNyZW1lbnRfUzFfYzApKSAqIG9mZnNldEFuZEtlcm5lbC55Owp9CmhhbGY0IEdhdXNzaWFuQ29udm9sdXRpb25fUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgY29sb3IgPSBoYWxmNCgwKTsKCWZsb2F0MiBjb29yZCA9IHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwoJZm9yIChpbnQgaT0wOyBpPDEzOyArK2kpIAoJewoJCWNvbG9yICs9IFNtb290aF9TMV9jMChfaW5wdXQsIGNvb3JkLCB1T2Zmc2V0c0FuZEtlcm5lbF9TMV9jMFtpXSk7Cgl9CglyZXR1cm4gY29sb3I7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBHYXVzc2lhbkNvbnZvbHV0aW9uX1MxX2MwKF9pbnB1dCk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzAgPSBoYWxmNCgxKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBNYXRyaXhFZmZlY3RfUzEob3V0cHV0Q29sb3JfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgoAAABsb2NhbENvb3JkAAAAAAAA","AYQA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAACABYQA6AAAEAAAAAAAIADQAAAAIAAAAAAAIIDA":"CgAAAExTS1PMAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0NCBpbkNpcmNsZUVkZ2U7Cm91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJdmluQ2lyY2xlRWRnZV9TMCA9IGluQ2lyY2xlRWRnZTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSB1bG9jYWxNYXRyaXhfUzAueHogKiBpblBvc2l0aW9uICsgdWxvY2FsTWF0cml4X1MwLnl3OwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgEAAAB5AwAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CmluIGZsb2F0NCB2aW5DaXJjbGVFZGdlX1MwOwppbiBoYWxmNCB2aW5Db2xvcl9TMDsKaGFsZjQgQ2lyY3VsYXJSUmVjdF9TMShoYWxmNCBfaW5wdXQpIAp7CglmbG9hdDIgZHh5MCA9IHVpbm5lclJlY3RfUzEuTFQgLSBza19GcmFnQ29vcmQueHk7CglmbG9hdDIgZHh5MSA9IHNrX0ZyYWdDb29yZC54eSAtIHVpbm5lclJlY3RfUzEuUkI7CglmbG9hdDIgZHh5ID0gbWF4KG1heChkeHkwLCBkeHkxKSwgMC4wKTsKCWhhbGYgYWxwaGEgPSBoYWxmKHNhdHVyYXRlKHVyYWRpdXNQbHVzSGFsZl9TMS54IC0gbGVuZ3RoKGR4eSkpKTsKCXJldHVybiBfaW5wdXQgKiBhbHBoYTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgQ2lyY2xlR2VvbWV0cnlQcm9jZXNzb3IKCWZsb2F0NCBjaXJjbGVFZGdlOwoJY2lyY2xlRWRnZSA9IHZpbkNpcmNsZUVkZ2VfUzA7CgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmluQ29sb3JfUzA7CglmbG9hdCBkID0gbGVuZ3RoKGNpcmNsZUVkZ2UueHkpOwoJaGFsZiBkaXN0YW5jZVRvT3V0ZXJFZGdlID0gaGFsZihjaXJjbGVFZGdlLnogKiAoMS4wIC0gZCkpOwoJaGFsZiBlZGdlQWxwaGEgPSBzYXR1cmF0ZShkaXN0YW5jZVRvT3V0ZXJFZGdlKTsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoZWRnZUFscGhhKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IENpcmN1bGFyUlJlY3RfUzEob3V0cHV0Q292ZXJhZ2VfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0X1MxOwoJfQp9CgAAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACgAAAGluUG9zaXRpb24AAAcAAABpbkNvbG9yAAwAAABpbkNpcmNsZUVkZ2UAAAAA","DAQAAAAAAABGAABAYAAQAIHCAIAYAQUBAEAAAAAAEAAAAAAAAAAAAAB2AAAAAAACAAAAAEBSAAAAA":"CgAAAExTS1MWAgAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQyIHVBdGxhc1NpemVJbnZfUzA7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiB1c2hvcnQyIGluVGV4dHVyZUNvb3JkczsKb3V0IGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKZmxhdCBvdXQgZmxvYXQgdlRleEluZGV4X1MwOwpvdXQgaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIEJpdG1hcFRleHQKCWludCB0ZXhJZHggPSAwOwoJZmxvYXQyIHVub3JtVGV4Q29vcmRzID0gZmxvYXQyKGluVGV4dHVyZUNvb3Jkcy54LCBpblRleHR1cmVDb29yZHMueSk7Cgl2VGV4dHVyZUNvb3Jkc19TMCA9IHVub3JtVGV4Q29vcmRzICogdUF0bGFzU2l6ZUludl9TMDsKCXZUZXhJbmRleF9TMCA9IGZsb2F0KHRleElkeCk7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJc2tfUG9zaXRpb24gPSBpblBvc2l0aW9uLnh5MDE7Cn0KAAAAAAAAsQEAAHVuaWZvcm0gc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MwOwppbiBmbG9hdDIgdlRleHR1cmVDb29yZHNfUzA7CmZsYXQgaW4gZmxvYXQgdlRleEluZGV4X1MwOwppbiBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIEJpdG1hcFRleHQKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWhhbGY0IHRleENvbG9yOwoJewoJCXRleENvbG9yID0gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB2VGV4dHVyZUNvb3Jkc19TMCkucnJycjsKCX0KCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gdGV4Q29sb3I7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAoAAABpblBvc2l0aW9uAAAHAAAAaW5Db2xvcgAPAAAAaW5UZXh0dXJlQ29vcmRzAAAAAAA=","HVIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAFIBQU7BTXIAAAAAACAWXW3ZEQAAAADAAAAAGATHIBICYCAAAEBP2LBIPAAAAAIAAAAAEARRALJ3F5SMAAAABQAAAABABTUEURMBAAACAH5FYUHQAAAAAAAEAAAAAZ4RGGRCQFAEAAAAAAAAAGARP2LVJPAAAAAAAAEAAAABSKRXZFAUHQAAAAAAAAAACAA4AAAACAAAAAAACCAYAA":"CgAAAExTS1OAAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBoYWxmNCBjb2xvcjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7CmZsYXQgb3V0IGhhbGY0IHZjb2xvcl9TMDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfNV9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfNV9TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzFfYzBfYzEpICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAAAAIIGAAB1bmlmb3JtIGhhbGY0IHVzdGFydF9TMV9jMF9jMDsKdW5pZm9ybSBoYWxmNCB1ZW5kX1MxX2MwX2MwOwp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzBfYzE7CnVuaWZvcm0gaGFsZjQgdWxlZnRCb3JkZXJDb2xvcl9TMV9jMDsKdW5pZm9ybSBoYWxmNCB1cmlnaHRCb3JkZXJDb2xvcl9TMV9jMDsKZmxhdCBpbiBoYWxmNCB2Y29sb3JfUzA7CmluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfNV9TMDsKaGFsZjQgU2luZ2xlSW50ZXJ2YWxDb2xvcml6ZXJfUzFfYzBfYzAoaGFsZjQgX2lucHV0LCBmbG9hdDIgX2Nvb3JkcykgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJZmxvYXQyIF90bXBfMV9jb29yZHMgPSBfY29vcmRzOwoJcmV0dXJuIGhhbGY0KG1peCh1c3RhcnRfUzFfYzBfYzAsIHVlbmRfUzFfYzBfYzAsIGhhbGYoX3RtcF8xX2Nvb3Jkcy54KSkpOwp9CmhhbGY0IExpbmVhckxheW91dF9TMV9jMF9jMV9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzJfaW5Db2xvciA9IF9pbnB1dDsKCWZsb2F0MiBfdG1wXzNfY29vcmRzID0gdlRyYW5zZm9ybWVkQ29vcmRzXzVfUzA7CglyZXR1cm4gaGFsZjQoaGFsZjQoaGFsZihfdG1wXzNfY29vcmRzLngpICsgMWUtMDUsIDEuMCwgMC4wLCAwLjApKTsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzFfYzBfYzEoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIExpbmVhckxheW91dF9TMV9jMF9jMV9jMChfaW5wdXQpOwp9CmhhbGY0IENsYW1wZWRHcmFkaWVudF9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzRfaW5Db2xvciA9IF9pbnB1dDsKCWhhbGY0IHQgPSBNYXRyaXhFZmZlY3RfUzFfYzBfYzEoX3RtcF80X2luQ29sb3IpOwoJaGFsZjQgb3V0Q29sb3I7CglpZiAoIWJvb2woaW50KDEpKSAmJiB0LnkgPCAwLjApIAoJewoJCW91dENvbG9yID0gaGFsZjQoMC4wKTsKCX0KCWVsc2UgaWYgKHQueCA8IDAuMCkgCgl7CgkJb3V0Q29sb3IgPSB1bGVmdEJvcmRlckNvbG9yX1MxX2MwOwoJfQoJZWxzZSBpZiAodC54ID4gMS4wKSAKCXsKCQlvdXRDb2xvciA9IHVyaWdodEJvcmRlckNvbG9yX1MxX2MwOwoJfQoJZWxzZSAKCXsKCQlvdXRDb2xvciA9IFNpbmdsZUludGVydmFsQ29sb3JpemVyX1MxX2MwX2MwKF90bXBfNF9pbkNvbG9yLCBmbG9hdDIoaGFsZjIodC54LCAwLjApKSk7Cgl9CglyZXR1cm4gaGFsZjQob3V0Q29sb3IpOwp9CmhhbGY0IERpc2FibGVDb3ZlcmFnZUFzQWxwaGFfUzEoaGFsZjQgX2lucHV0KSAKewoJX2lucHV0ID0gQ2xhbXBlZEdyYWRpZW50X1MxX2MwKF9pbnB1dCk7CgloYWxmNCBfdG1wXzVfaW5Db2xvciA9IF9pbnB1dDsKCXJldHVybiBoYWxmNChfaW5wdXQpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gRGlzYWJsZUNvdmVyYWdlQXNBbHBoYV9TMShvdXRwdXRDb2xvcl9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0X1MxICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAgAAABwb3NpdGlvbgUAAABjb2xvcgAAAAoAAABsb2NhbENvb3JkAAAAAAAA","HUQACAAAAAMAADAAAIOAAAH677776IZOCAAP577777777777777777YBAAAAAAAAAAAHEADZAAAAAAIAAAAAAOQAAAAAAAQAAAABAMQAAAAAA":"CgAAAExTS1PPAAAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7Cm91dCBoYWxmNCB2Y29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cn0KAAEAAACbAgAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CmluIGhhbGY0IHZjb2xvcl9TMDsKaGFsZjQgQ2lyY3VsYXJSUmVjdF9TMShoYWxmNCBfaW5wdXQpIAp7CglmbG9hdDIgZHh5MCA9IHVpbm5lclJlY3RfUzEuTFQgLSBza19GcmFnQ29vcmQueHk7CglmbG9hdDIgZHh5MSA9IHNrX0ZyYWdDb29yZC54eSAtIHVpbm5lclJlY3RfUzEuUkI7CglmbG9hdDIgZHh5ID0gbWF4KG1heChkeHkwLCBkeHkxKSwgMC4wKTsKCWhhbGYgYWxwaGEgPSBoYWxmKHNhdHVyYXRlKHVyYWRpdXNQbHVzSGFsZl9TMS54IC0gbGVuZ3RoKGR4eSkpKTsKCXJldHVybiBfaW5wdXQgKiBhbHBoYTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IENpcmN1bGFyUlJlY3RfUzEob3V0cHV0Q292ZXJhZ2VfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0X1MxOwoJfQp9CgABAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgUAAABjb2xvcgAAAAAAAAA=","DAQAAAAAAABGAABAYAAQAIHCAIAYAQUBAEAAAAAAEAAAAAAAAAAAAIAHSADQAAAQAAAAAAFAAMAAAABAAAAAAABBAM":"CgAAAExTS1MWAgAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQyIHVBdGxhc1NpemVJbnZfUzA7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiB1c2hvcnQyIGluVGV4dHVyZUNvb3JkczsKb3V0IGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKZmxhdCBvdXQgZmxvYXQgdlRleEluZGV4X1MwOwpvdXQgaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIEJpdG1hcFRleHQKCWludCB0ZXhJZHggPSAwOwoJZmxvYXQyIHVub3JtVGV4Q29vcmRzID0gZmxvYXQyKGluVGV4dHVyZUNvb3Jkcy54LCBpblRleHR1cmVDb29yZHMueSk7Cgl2VGV4dHVyZUNvb3Jkc19TMCA9IHVub3JtVGV4Q29vcmRzICogdUF0bGFzU2l6ZUludl9TMDsKCXZUZXhJbmRleF9TMCA9IGZsb2F0KHRleElkeCk7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJc2tfUG9zaXRpb24gPSBpblBvc2l0aW9uLnh5MDE7Cn0KAAABAAAAQAMAAHVuaWZvcm0gZmxvYXQ0IHVpbm5lclJlY3RfUzE7CnVuaWZvcm0gaGFsZjIgdXJhZGl1c1BsdXNIYWxmX1MxOwp1bmlmb3JtIHNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMDsKaW4gZmxvYXQyIHZUZXh0dXJlQ29vcmRzX1MwOwpmbGF0IGluIGZsb2F0IHZUZXhJbmRleF9TMDsKaW4gaGFsZjQgdmluQ29sb3JfUzA7CmhhbGY0IENpcmN1bGFyUlJlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MxLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MxLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CgloYWxmIGFscGhhID0gaGFsZihzYXR1cmF0ZSh1cmFkaXVzUGx1c0hhbGZfUzEueCAtIGxlbmd0aChkeHkpKSk7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIEJpdG1hcFRleHQKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWhhbGY0IHRleENvbG9yOwoJewoJCXRleENvbG9yID0gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB2VGV4dHVyZUNvb3Jkc19TMCkucnJycjsKCX0KCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gdGV4Q29sb3I7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBDaXJjdWxhclJSZWN0X1MxKG91dHB1dENvdmVyYWdlX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoBAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAoAAABpblBvc2l0aW9uAAAHAAAAaW5Db2xvcgAPAAAAaW5UZXh0dXJlQ29vcmRzAAAAAAA=","HUIAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAAAAAALUAQMAAAAAAAAAEAAAABJYQAAAAAAAAIAAAAAWCBAAAABAAAAANAECAZAAAAAAAAAAAFAAMAAAABAAAAAAABBAM":"CgAAAExTS1M2AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAAAAPAEAAB1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzBfYzA7CnVuaWZvcm0gaGFsZjIgdUluY3JlbWVudF9TMV9jMDsKdW5pZm9ybSBoYWxmMiB1T2Zmc2V0c0FuZEtlcm5lbF9TMV9jMFsxM107CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKdW5pZm9ybSBzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7CmluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMF9jMF9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBfY29vcmRzKSAKewoJcmV0dXJuIHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMSwgX2Nvb3Jkcyk7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxX2MwX2MwKGhhbGY0IF9pbnB1dCwgZmxvYXQyIF9jb29yZHMpIAp7CglyZXR1cm4gVGV4dHVyZUVmZmVjdF9TMV9jMF9jMF9jMChfaW5wdXQsIGZsb2F0M3gyKHVtYXRyaXhfUzFfYzBfYzApICogX2Nvb3Jkcy54eTEpOwp9CmhhbGY0IFNtb290aF9TMV9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBjb29yZCwgaGFsZjIgb2Zmc2V0QW5kS2VybmVsKSAKewoJcmV0dXJuIE1hdHJpeEVmZmVjdF9TMV9jMF9jMChfaW5wdXQsIChjb29yZCArIG9mZnNldEFuZEtlcm5lbC54ICogdUluY3JlbWVudF9TMV9jMCkpICogb2Zmc2V0QW5kS2VybmVsLnk7Cn0KaGFsZjQgR2F1c3NpYW5Db252b2x1dGlvbl9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBjb2xvciA9IGhhbGY0KDApOwoJZmxvYXQyIGNvb3JkID0gdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7Cglmb3IgKGludCBpPTA7IGk8MTM7ICsraSkgCgl7CgkJY29sb3IgKz0gU21vb3RoX1MxX2MwKF9pbnB1dCwgY29vcmQsIHVPZmZzZXRzQW5kS2VybmVsX1MxX2MwW2ldKTsKCX0KCXJldHVybiBjb2xvcjsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIEdhdXNzaWFuQ29udm9sdXRpb25fUzFfYzAoX2lucHV0KTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMCA9IGhhbGY0KDEpOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IE1hdHJpeEVmZmVjdF9TMShvdXRwdXRDb2xvcl9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0X1MxICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAIAAAAcG9zaXRpb24KAAAAbG9jYWxDb29yZAAAAAAAAA==","HUQACAAAAAMAADAAAIOAAAH677776IZOCAAP577777777777777777YBAAAAAAAAAAAKAAYAAAACAAAAAAACCAYAAA":"CgAAAExTS1PPAAAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7Cm91dCBoYWxmNCB2Y29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cn0KAAAAAAAMAQAAaW4gaGFsZjQgdmNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAAAAAAAA==","CMRQCIAABBYAAAEIXBAAACDQMAABRAFAAAAAAAAAAAAAAAEABYAAAAEAAAAAAAEEBQAAAAA":"CgAAAExTS1MyAgAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0MiBpbkVsbGlwc2VPZmZzZXQ7CmluIGZsb2F0NCBpbkVsbGlwc2VSYWRpaTsKb3V0IGZsb2F0MiB2RWxsaXBzZU9mZnNldHNfUzA7Cm91dCBmbG9hdDQgdkVsbGlwc2VSYWRpaV9TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBFbGxpcHNlR2VvbWV0cnlQcm9jZXNzb3IKCXZFbGxpcHNlT2Zmc2V0c19TMCA9IGluRWxsaXBzZU9mZnNldDsKCXZFbGxpcHNlUmFkaWlfUzAgPSBpbkVsbGlwc2VSYWRpaTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSB1bG9jYWxNYXRyaXhfUzAueHogKiBpblBvc2l0aW9uICsgdWxvY2FsTWF0cml4X1MwLnl3OwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgAAAAAAAHIDAABpbiBmbG9hdDIgdkVsbGlwc2VPZmZzZXRzX1MwOwppbiBmbG9hdDQgdkVsbGlwc2VSYWRpaV9TMDsKaW4gaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBFbGxpcHNlR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWZsb2F0MiBvZmZzZXQgPSB2RWxsaXBzZU9mZnNldHNfUzAueHk7CglvZmZzZXQgKj0gdkVsbGlwc2VSYWRpaV9TMC54eTsKCWZsb2F0IHRlc3QgPSBkb3Qob2Zmc2V0LCBvZmZzZXQpIC0gMS4wOwoJZmxvYXQyIGdyYWQgPSAyLjAqb2Zmc2V0KnZFbGxpcHNlUmFkaWlfUzAueHk7CglmbG9hdCBncmFkX2RvdCA9IGRvdChncmFkLCBncmFkKTsKCWdyYWRfZG90ID0gbWF4KGdyYWRfZG90LCAxLjE3NTVlLTM4KTsKCWZsb2F0IGludmxlbiA9IGludmVyc2VzcXJ0KGdyYWRfZG90KTsKCWZsb2F0IGVkZ2VBbHBoYSA9IHNhdHVyYXRlKDAuNS10ZXN0Kmludmxlbik7CglvZmZzZXQgPSB2RWxsaXBzZU9mZnNldHNfUzAueHkqdkVsbGlwc2VSYWRpaV9TMC56dzsKCXRlc3QgPSBkb3Qob2Zmc2V0LCBvZmZzZXQpIC0gMS4wOwoJZ3JhZCA9IDIuMCpvZmZzZXQqdkVsbGlwc2VSYWRpaV9TMC56dzsKCWdyYWRfZG90ID0gZG90KGdyYWQsIGdyYWQpOwoJaW52bGVuID0gaW52ZXJzZXNxcnQoZ3JhZF9kb3QpOwoJZWRnZUFscGhhICo9IHNhdHVyYXRlKDAuNSt0ZXN0Kmludmxlbik7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGhhbGYoZWRnZUFscGhhKSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACgAAAGluUG9zaXRpb24AAAcAAABpbkNvbG9yAA8AAABpbkVsbGlwc2VPZmZzZXQADgAAAGluRWxsaXBzZVJhZGlpAAAAAAAA","HUIAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAAAAAALUAQAAEAQAAAAGQCBAMQAAAIAAAAAACQAGAAAAAQAAAAAAAQQGAAAAA":"CgAAAExTS1M2AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAAAAHADAAB1bmlmb3JtIGZsb2F0NCB1Y2xhbXBfUzFfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKdW5pZm9ybSBzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7CmluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CglmbG9hdDIgaW5Db29yZCA9IHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwoJZmxvYXQyIHN1YnNldENvb3JkOwoJc3Vic2V0Q29vcmQueCA9IGluQ29vcmQueDsKCXN1YnNldENvb3JkLnkgPSBpbkNvb3JkLnk7CglmbG9hdDIgY2xhbXBlZENvb3JkOwoJY2xhbXBlZENvb3JkLnggPSBzdWJzZXRDb29yZC54OwoJY2xhbXBlZENvb3JkLnkgPSBjbGFtcChzdWJzZXRDb29yZC55LCB1Y2xhbXBfUzFfYzAueSwgdWNsYW1wX1MxX2MwLncpOwoJaGFsZjQgdGV4dHVyZUNvbG9yID0gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MxLCBjbGFtcGVkQ29vcmQpOwoJcmV0dXJuIHRleHR1cmVDb2xvcjsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzAoX2lucHV0KTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMCA9IGhhbGY0KDEpOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IE1hdHJpeEVmZmVjdF9TMShvdXRwdXRDb2xvcl9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0X1MxICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAIAAAAcG9zaXRpb24KAAAAbG9jYWxDb29yZAAAAAAAAA==","HVJAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAABAAAAAABBAMABAAOAAAABAAAAAAABBAMAAA":"CgAAAExTS1MjAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpmbGF0IG91dCBoYWxmNCB2Y29sb3JfUzA7Cm91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7Cgl2bG9jYWxDb29yZF9TMCA9IGxvY2FsQ29vcmQ7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cn0KAAAAAADQAQAAdW5pZm9ybSBzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzA7CmZsYXQgaW4gaGFsZjQgdmNvbG9yX1MwOwppbiBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7CglmbG9hdDIgdGV4Q29vcmQ7Cgl0ZXhDb29yZCA9IHZsb2NhbENvb3JkX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSAoKHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMCwgdGV4Q29vcmQpICogb3V0cHV0Q29sb3JfUzApKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAgAAABwb3NpdGlvbgUAAABjb2xvcgAAAAoAAABsb2NhbENvb3JkAAAAAAAA","AYQA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAACAFBQATAAAAAAFAAMAAAABAAAAAAABBAMAAAAA":"CgAAAExTS1PMAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0NCBpbkNpcmNsZUVkZ2U7Cm91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJdmluQ2lyY2xlRWRnZV9TMCA9IGluQ2lyY2xlRWRnZTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSB1bG9jYWxNYXRyaXhfUzAueHogKiBpblBvc2l0aW9uICsgdWxvY2FsTWF0cml4X1MwLnl3OwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgEAAABABAAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBmbG9hdDIgdWludlJhZGlpWFlfUzE7CmluIGZsb2F0NCB2aW5DaXJjbGVFZGdlX1MwOwppbiBoYWxmNCB2aW5Db2xvcl9TMDsKaGFsZjQgRWxsaXB0aWNhbFJSZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCWZsb2F0MiBkeHkwID0gdWlubmVyUmVjdF9TMS5MVCAtIHNrX0ZyYWdDb29yZC54eTsKCWZsb2F0MiBkeHkxID0gc2tfRnJhZ0Nvb3JkLnh5IC0gdWlubmVyUmVjdF9TMS5SQjsKCWZsb2F0MiBkeHkgPSBtYXgobWF4KGR4eTAsIGR4eTEpLCAwLjApOwoJZmxvYXQyIFogPSBkeHkgKiB1aW52UmFkaWlYWV9TMS54eTsKCWhhbGYgaW1wbGljaXQgPSBoYWxmKGRvdChaLCBkeHkpIC0gMS4wKTsKCWhhbGYgZ3JhZF9kb3QgPSBoYWxmKDQuMCAqIGRvdChaLCBaKSk7CglncmFkX2RvdCA9IG1heChncmFkX2RvdCwgMS4wZS00KTsKCWhhbGYgYXBwcm94X2Rpc3QgPSBpbXBsaWNpdCAqIGhhbGYoaW52ZXJzZXNxcnQoZ3JhZF9kb3QpKTsKCWhhbGYgYWxwaGEgPSBjbGFtcCgwLjUgKyBhcHByb3hfZGlzdCwgMC4wLCAxLjApOwoJcmV0dXJuIF9pbnB1dCAqIGFscGhhOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQ0IGNpcmNsZUVkZ2U7CgljaXJjbGVFZGdlID0gdmluQ2lyY2xlRWRnZV9TMDsKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWZsb2F0IGQgPSBsZW5ndGgoY2lyY2xlRWRnZS54eSk7CgloYWxmIGRpc3RhbmNlVG9PdXRlckVkZ2UgPSBoYWxmKGNpcmNsZUVkZ2UueiAqICgxLjAgLSBkKSk7CgloYWxmIGVkZ2VBbHBoYSA9IHNhdHVyYXRlKGRpc3RhbmNlVG9PdXRlckVkZ2UpOwoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChlZGdlQWxwaGEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gRWxsaXB0aWNhbFJSZWN0X1MxKG91dHB1dENvdmVyYWdlX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoBAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAoAAABpblBvc2l0aW9uAAAHAAAAaW5Db2xvcgAMAAAAaW5DaXJjbGVFZGdlAAAAAA==","HUJAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAQAAAAAEQQGABZAA6IAAAAACAAAAAADUAAAAAAAEAAAAAIDEAAAAAAA":"CgAAAExTS1PlAAAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7Cm91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmxvY2FsQ29vcmRfUzAgPSBsb2NhbENvb3JkOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAAEAAAA3AwAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CnVuaWZvcm0gc2FtcGxlckV4dGVybmFsT0VTIHVUZXh0dXJlU2FtcGxlcl8wX1MwOwppbiBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CmhhbGY0IENpcmN1bGFyUlJlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MxLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MxLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CgloYWxmIGFscGhhID0gaGFsZihzYXR1cmF0ZSh1cmFkaXVzUGx1c0hhbGZfUzEueCAtIGxlbmd0aChkeHkpKSk7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzAgPSBoYWxmNCgxKTsKCWZsb2F0MiB0ZXhDb29yZDsKCXRleENvb3JkID0gdmxvY2FsQ29vcmRfUzA7CglvdXRwdXRDb2xvcl9TMCA9ICgoc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB0ZXhDb29yZCkgKiBoYWxmNCgxKSkpOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IENpcmN1bGFyUlJlY3RfUzEob3V0cHV0Q292ZXJhZ2VfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0X1MxOwoJfQp9CgABAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgoAAABsb2NhbENvb3JkAAAAAAAA","FAAQMYAAMAAAEADAAABAEYAAAICIAB5AABQAAAQAMAAAEATAAABAIIGAAEDCBYQCA4AAAAAAEAB5AAAAACQHEB4XIQAQAADQAAAABAAAAAAABAEMVDOMCJKRAAAAAHAAAAAAAAAAACQAGAAAAAQAAAAAAAQQGAAA":"CgAAAExTS1PUCwAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0NCByYWRpaV9zZWxlY3RvcjsKaW4gZmxvYXQ0IGNvcm5lcl9hbmRfcmFkaXVzX291dHNldHM7CmluIGZsb2F0NCBhYV9ibG9hdF9hbmRfY292ZXJhZ2U7CmluIGZsb2F0NCByYWRpaV94OwppbiBmbG9hdDQgcmFkaWlfeTsKaW4gZmxvYXQ0IHNrZXc7CmluIGZsb2F0MiB0cmFuc2xhdGVfYW5kX2xvY2Fscm90YXRlOwppbiBoYWxmNCBjb2xvcjsKZmxhdCBvdXQgaGFsZjQgdmNvbG9yX1MwOwpvdXQgZmxvYXQyIHZhcmNjb29yZF9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgRmlsbFJSZWN0T3A6OlByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7CglmbG9hdCBhYV9ibG9hdF9tdWx0aXBsaWVyID0gMTsKCWZsb2F0MiBjb3JuZXIgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnh5OwoJZmxvYXQyIHJhZGl1c19vdXRzZXQgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnp3OwoJZmxvYXQyIGFhX2Jsb2F0X2RpcmVjdGlvbiA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS54eTsKCWZsb2F0IGlzX2xpbmVhcl9jb3ZlcmFnZSA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS53OwoJZmxvYXQyIHBpeGVsbGVuZ3RoID0gaW52ZXJzZXNxcnQoZmxvYXQyKGRvdChza2V3Lnh6LCBza2V3Lnh6KSwgZG90KHNrZXcueXcsIHNrZXcueXcpKSk7CglmbG9hdDQgbm9ybWFsaXplZF9heGlzX2RpcnMgPSBza2V3ICogcGl4ZWxsZW5ndGgueHl4eTsKCWZsb2F0MiBheGlzd2lkdGhzID0gKGFicyhub3JtYWxpemVkX2F4aXNfZGlycy54eSkgKyBhYnMobm9ybWFsaXplZF9heGlzX2RpcnMuencpKTsKCWZsb2F0MiBhYV9ibG9hdHJhZGl1cyA9IGF4aXN3aWR0aHMgKiBwaXhlbGxlbmd0aCAqIC41OwoJZmxvYXQ0IHJhZGlpX2FuZF9uZWlnaGJvcnMgPSByYWRpaV9zZWxlY3RvciogZmxvYXQ0eDQocmFkaWlfeCwgcmFkaWlfeSwgcmFkaWlfeC55eHd6LCByYWRpaV95Lnd6eXgpOwoJZmxvYXQyIHJhZGlpID0gcmFkaWlfYW5kX25laWdoYm9ycy54eTsKCWZsb2F0MiBuZWlnaGJvcl9yYWRpaSA9IHJhZGlpX2FuZF9uZWlnaGJvcnMuenc7CglmbG9hdCBjb3ZlcmFnZV9tdWx0aXBsaWVyID0gMTsKCWlmIChhbnkoZ3JlYXRlclRoYW4oYWFfYmxvYXRyYWRpdXMsIGZsb2F0MigxKSkpKSAKCXsKCQljb3JuZXIgPSBtYXgoYWJzKGNvcm5lciksIGFhX2Jsb2F0cmFkaXVzKSAqIHNpZ24oY29ybmVyKTsKCQljb3ZlcmFnZV9tdWx0aXBsaWVyID0gMSAvIChtYXgoYWFfYmxvYXRyYWRpdXMueCwgMSkgKiBtYXgoYWFfYmxvYXRyYWRpdXMueSwgMSkpOwoJCXJhZGlpID0gZmxvYXQyKDApOwoJfQoJZmxvYXQgY292ZXJhZ2UgPSBhYV9ibG9hdF9hbmRfY292ZXJhZ2UuejsKCWlmIChhbnkobGVzc1RoYW4ocmFkaWksIGFhX2Jsb2F0cmFkaXVzICogMS41KSkpIAoJewoJCXJhZGlpID0gZmxvYXQyKDApOwoJCWFhX2Jsb2F0X2RpcmVjdGlvbiA9IHNpZ24oY29ybmVyKTsKCQlpZiAoY292ZXJhZ2UgPiAuNSkgCgkJewoJCQlhYV9ibG9hdF9kaXJlY3Rpb24gPSAtYWFfYmxvYXRfZGlyZWN0aW9uOwoJCX0KCQlpc19saW5lYXJfY292ZXJhZ2UgPSAxOwoJfQoJZWxzZSAKCXsKCQlyYWRpaSA9IGNsYW1wKHJhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQluZWlnaGJvcl9yYWRpaSA9IGNsYW1wKG5laWdoYm9yX3JhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQlmbG9hdDIgc3BhY2luZyA9IDIgLSByYWRpaSAtIG5laWdoYm9yX3JhZGlpOwoJCWZsb2F0MiBleHRyYV9wYWQgPSBtYXgocGl4ZWxsZW5ndGggKiAuMDYyNSAtIHNwYWNpbmcsIGZsb2F0MigwKSk7CgkJcmFkaWkgLT0gZXh0cmFfcGFkICogLjU7Cgl9CglmbG9hdDIgYWFfb3V0c2V0ID0gYWFfYmxvYXRfZGlyZWN0aW9uICogYWFfYmxvYXRyYWRpdXMgKiBhYV9ibG9hdF9tdWx0aXBsaWVyOwoJZmxvYXQyIHZlcnRleHBvcyA9IGNvcm5lciArIHJhZGl1c19vdXRzZXQgKiByYWRpaSArIGFhX291dHNldDsKCWlmIChjb3ZlcmFnZSA+IC41KSAKCXsKCQlpZiAoYWFfYmxvYXRfZGlyZWN0aW9uLnggIT0gMCAmJiB2ZXJ0ZXhwb3MueCAqIGNvcm5lci54IDwgMCkgCgkJewoJCQlmbG9hdCBiYWNrc2V0ID0gYWJzKHZlcnRleHBvcy54KTsKCQkJdmVydGV4cG9zLnggPSAwOwoJCQl2ZXJ0ZXhwb3MueSArPSBiYWNrc2V0ICogc2lnbihjb3JuZXIueSkgKiBwaXhlbGxlbmd0aC55L3BpeGVsbGVuZ3RoLng7CgkJCWNvdmVyYWdlID0gKGNvdmVyYWdlIC0gLjUpICogYWJzKGNvcm5lci54KSAvIChhYnMoY29ybmVyLngpICsgYmFja3NldCkgKyAuNTsKCQl9CgkJaWYgKGFhX2Jsb2F0X2RpcmVjdGlvbi55ICE9IDAgJiYgdmVydGV4cG9zLnkgKiBjb3JuZXIueSA8IDApIAoJCXsKCQkJZmxvYXQgYmFja3NldCA9IGFicyh2ZXJ0ZXhwb3MueSk7CgkJCXZlcnRleHBvcy55ID0gMDsKCQkJdmVydGV4cG9zLnggKz0gYmFja3NldCAqIHNpZ24oY29ybmVyLngpICogcGl4ZWxsZW5ndGgueC9waXhlbGxlbmd0aC55OwoJCQljb3ZlcmFnZSA9IChjb3ZlcmFnZSAtIC41KSAqIGFicyhjb3JuZXIueSkgLyAoYWJzKGNvcm5lci55KSArIGJhY2tzZXQpICsgLjU7CgkJfQoJfQoJZmxvYXQyeDIgc2tld21hdHJpeCA9IGZsb2F0MngyKHNrZXcueHksIHNrZXcuencpOwoJZmxvYXQyIGRldmNvb3JkID0gdmVydGV4cG9zICogc2tld21hdHJpeCArIHRyYW5zbGF0ZV9hbmRfbG9jYWxyb3RhdGUueHk7CglpZiAoMCAhPSBpc19saW5lYXJfY292ZXJhZ2UpIAoJewoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MigwLCBjb3ZlcmFnZSAqIGNvdmVyYWdlX211bHRpcGxpZXIpOwoJfQoJZWxzZSAKCXsKCQlmbG9hdDIgYXJjY29vcmQgPSAxIC0gYWJzKHJhZGl1c19vdXRzZXQpICsgYWFfb3V0c2V0L3JhZGlpICogY29ybmVyOwoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MihhcmNjb29yZC54KzEsIGFyY2Nvb3JkLnkpOwoJfQoJc2tfUG9zaXRpb24gPSBkZXZjb29yZC54eTAxOwp9CgEAAABRBQAAY29uc3QgaW50IGtGaWxsQUFfUzFfYzAgPSAxOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQldfUzFfYzAgPSAyOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQUFfUzFfYzAgPSAzOwp1bmlmb3JtIGZsb2F0NCB1Y2lyY2xlX1MxX2MwOwpmbGF0IGluIGhhbGY0IHZjb2xvcl9TMDsKaW4gZmxvYXQyIHZhcmNjb29yZF9TMDsKaGFsZjQgQ2lyY2xlX1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJaGFsZiBkOwoJaWYgKGludCgzKSA9PSBrSW52ZXJzZUZpbGxCV19TMV9jMCB8fCBpbnQoMykgPT0ga0ludmVyc2VGaWxsQUFfUzFfYzApIAoJewoJCWQgPSBoYWxmKChsZW5ndGgoKHVjaXJjbGVfUzFfYzAueHkgLSBza19GcmFnQ29vcmQueHkpICogdWNpcmNsZV9TMV9jMC53KSAtIDEuMCkgKiB1Y2lyY2xlX1MxX2MwLnopOwoJfQoJZWxzZSAKCXsKCQlkID0gaGFsZigoMS4wIC0gbGVuZ3RoKCh1Y2lyY2xlX1MxX2MwLnh5IC0gc2tfRnJhZ0Nvb3JkLnh5KSAqIHVjaXJjbGVfUzFfYzAudykpICogdWNpcmNsZV9TMV9jMC56KTsKCX0KCXJldHVybiBoYWxmNChoYWxmNChpbnQoMykgPT0ga0ZpbGxBQV9TMV9jMCB8fCBpbnQoMykgPT0ga0ludmVyc2VGaWxsQUFfUzFfYzAgPyBzYXR1cmF0ZShkKSA6IGhhbGYoZCA+IDAuNSA/IDEgOiAwKSkpOwp9CmhhbGY0IEJsZW5kX1MxKGhhbGY0IF9zcmMsIGhhbGY0IF9kc3QpIAp7CglyZXR1cm4gYmxlbmRfbW9kdWxhdGUoX3NyYywgQ2lyY2xlX1MxX2MwKF9zcmMpKTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgRmlsbFJSZWN0T3A6OlByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWZsb2F0IHhfcGx1c18xPXZhcmNjb29yZF9TMC54LCB5PXZhcmNjb29yZF9TMC55OwoJaGFsZiBjb3ZlcmFnZTsKCWlmICgwID09IHhfcGx1c18xKSAKCXsKCQljb3ZlcmFnZSA9IGhhbGYoeSk7Cgl9CgllbHNlIAoJewoJCWZsb2F0IGZuID0geF9wbHVzXzEgKiAoeF9wbHVzXzEgLSAyKTsKCQlmbiA9IGZtYSh5LHksIGZuKTsKCQlmbG9hdCBmbndpZHRoID0gZndpZHRoKGZuKTsKCQljb3ZlcmFnZSA9IC41IC0gaGFsZihmbi9mbndpZHRoKTsKCQljb3ZlcmFnZSA9IGNsYW1wKGNvdmVyYWdlLCAwLCAxKTsKCX0KCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoY292ZXJhZ2UpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gQmxlbmRfUzEob3V0cHV0Q292ZXJhZ2VfUzAsIGhhbGY0KDEpKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoAAAABAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAACAAAAA4AAAByYWRpaV9zZWxlY3RvcgAAGQAAAGNvcm5lcl9hbmRfcmFkaXVzX291dHNldHMAAAAVAAAAYWFfYmxvYXRfYW5kX2NvdmVyYWdlAAAABwAAAHJhZGlpX3gABwAAAHJhZGlpX3kABAAAAHNrZXcZAAAAdHJhbnNsYXRlX2FuZF9sb2NhbHJvdGF0ZQAAAAUAAABjb2xvcgAAAAAAAAA=","HUQAAAAAAAMAADAAAIOAAAH677776IZOCAAP577777777777777777YBAAAAAAAAAAAKAAYAAAACAAAAAAACCAYAAA":"CgAAAExTS1PUAAAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmZsYXQgb3V0IGhhbGY0IHZjb2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKfQoAAAAAEQEAAGZsYXQgaW4gaGFsZjQgdmNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAAAAAAAA==","FAAQMYAAMAAAEADAAABAEYAAAICIAB5AABQAAAQAMAAAEATAAABAIIGAAEDCBYQCA4AAAAAAAA5AAAAAAABAAAAACAZAAAAA":"CgAAAExTS1PUCwAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0NCByYWRpaV9zZWxlY3RvcjsKaW4gZmxvYXQ0IGNvcm5lcl9hbmRfcmFkaXVzX291dHNldHM7CmluIGZsb2F0NCBhYV9ibG9hdF9hbmRfY292ZXJhZ2U7CmluIGZsb2F0NCByYWRpaV94OwppbiBmbG9hdDQgcmFkaWlfeTsKaW4gZmxvYXQ0IHNrZXc7CmluIGZsb2F0MiB0cmFuc2xhdGVfYW5kX2xvY2Fscm90YXRlOwppbiBoYWxmNCBjb2xvcjsKZmxhdCBvdXQgaGFsZjQgdmNvbG9yX1MwOwpvdXQgZmxvYXQyIHZhcmNjb29yZF9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgRmlsbFJSZWN0T3A6OlByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7CglmbG9hdCBhYV9ibG9hdF9tdWx0aXBsaWVyID0gMTsKCWZsb2F0MiBjb3JuZXIgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnh5OwoJZmxvYXQyIHJhZGl1c19vdXRzZXQgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnp3OwoJZmxvYXQyIGFhX2Jsb2F0X2RpcmVjdGlvbiA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS54eTsKCWZsb2F0IGlzX2xpbmVhcl9jb3ZlcmFnZSA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS53OwoJZmxvYXQyIHBpeGVsbGVuZ3RoID0gaW52ZXJzZXNxcnQoZmxvYXQyKGRvdChza2V3Lnh6LCBza2V3Lnh6KSwgZG90KHNrZXcueXcsIHNrZXcueXcpKSk7CglmbG9hdDQgbm9ybWFsaXplZF9heGlzX2RpcnMgPSBza2V3ICogcGl4ZWxsZW5ndGgueHl4eTsKCWZsb2F0MiBheGlzd2lkdGhzID0gKGFicyhub3JtYWxpemVkX2F4aXNfZGlycy54eSkgKyBhYnMobm9ybWFsaXplZF9heGlzX2RpcnMuencpKTsKCWZsb2F0MiBhYV9ibG9hdHJhZGl1cyA9IGF4aXN3aWR0aHMgKiBwaXhlbGxlbmd0aCAqIC41OwoJZmxvYXQ0IHJhZGlpX2FuZF9uZWlnaGJvcnMgPSByYWRpaV9zZWxlY3RvciogZmxvYXQ0eDQocmFkaWlfeCwgcmFkaWlfeSwgcmFkaWlfeC55eHd6LCByYWRpaV95Lnd6eXgpOwoJZmxvYXQyIHJhZGlpID0gcmFkaWlfYW5kX25laWdoYm9ycy54eTsKCWZsb2F0MiBuZWlnaGJvcl9yYWRpaSA9IHJhZGlpX2FuZF9uZWlnaGJvcnMuenc7CglmbG9hdCBjb3ZlcmFnZV9tdWx0aXBsaWVyID0gMTsKCWlmIChhbnkoZ3JlYXRlclRoYW4oYWFfYmxvYXRyYWRpdXMsIGZsb2F0MigxKSkpKSAKCXsKCQljb3JuZXIgPSBtYXgoYWJzKGNvcm5lciksIGFhX2Jsb2F0cmFkaXVzKSAqIHNpZ24oY29ybmVyKTsKCQljb3ZlcmFnZV9tdWx0aXBsaWVyID0gMSAvIChtYXgoYWFfYmxvYXRyYWRpdXMueCwgMSkgKiBtYXgoYWFfYmxvYXRyYWRpdXMueSwgMSkpOwoJCXJhZGlpID0gZmxvYXQyKDApOwoJfQoJZmxvYXQgY292ZXJhZ2UgPSBhYV9ibG9hdF9hbmRfY292ZXJhZ2UuejsKCWlmIChhbnkobGVzc1RoYW4ocmFkaWksIGFhX2Jsb2F0cmFkaXVzICogMS41KSkpIAoJewoJCXJhZGlpID0gZmxvYXQyKDApOwoJCWFhX2Jsb2F0X2RpcmVjdGlvbiA9IHNpZ24oY29ybmVyKTsKCQlpZiAoY292ZXJhZ2UgPiAuNSkgCgkJewoJCQlhYV9ibG9hdF9kaXJlY3Rpb24gPSAtYWFfYmxvYXRfZGlyZWN0aW9uOwoJCX0KCQlpc19saW5lYXJfY292ZXJhZ2UgPSAxOwoJfQoJZWxzZSAKCXsKCQlyYWRpaSA9IGNsYW1wKHJhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQluZWlnaGJvcl9yYWRpaSA9IGNsYW1wKG5laWdoYm9yX3JhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQlmbG9hdDIgc3BhY2luZyA9IDIgLSByYWRpaSAtIG5laWdoYm9yX3JhZGlpOwoJCWZsb2F0MiBleHRyYV9wYWQgPSBtYXgocGl4ZWxsZW5ndGggKiAuMDYyNSAtIHNwYWNpbmcsIGZsb2F0MigwKSk7CgkJcmFkaWkgLT0gZXh0cmFfcGFkICogLjU7Cgl9CglmbG9hdDIgYWFfb3V0c2V0ID0gYWFfYmxvYXRfZGlyZWN0aW9uICogYWFfYmxvYXRyYWRpdXMgKiBhYV9ibG9hdF9tdWx0aXBsaWVyOwoJZmxvYXQyIHZlcnRleHBvcyA9IGNvcm5lciArIHJhZGl1c19vdXRzZXQgKiByYWRpaSArIGFhX291dHNldDsKCWlmIChjb3ZlcmFnZSA+IC41KSAKCXsKCQlpZiAoYWFfYmxvYXRfZGlyZWN0aW9uLnggIT0gMCAmJiB2ZXJ0ZXhwb3MueCAqIGNvcm5lci54IDwgMCkgCgkJewoJCQlmbG9hdCBiYWNrc2V0ID0gYWJzKHZlcnRleHBvcy54KTsKCQkJdmVydGV4cG9zLnggPSAwOwoJCQl2ZXJ0ZXhwb3MueSArPSBiYWNrc2V0ICogc2lnbihjb3JuZXIueSkgKiBwaXhlbGxlbmd0aC55L3BpeGVsbGVuZ3RoLng7CgkJCWNvdmVyYWdlID0gKGNvdmVyYWdlIC0gLjUpICogYWJzKGNvcm5lci54KSAvIChhYnMoY29ybmVyLngpICsgYmFja3NldCkgKyAuNTsKCQl9CgkJaWYgKGFhX2Jsb2F0X2RpcmVjdGlvbi55ICE9IDAgJiYgdmVydGV4cG9zLnkgKiBjb3JuZXIueSA8IDApIAoJCXsKCQkJZmxvYXQgYmFja3NldCA9IGFicyh2ZXJ0ZXhwb3MueSk7CgkJCXZlcnRleHBvcy55ID0gMDsKCQkJdmVydGV4cG9zLnggKz0gYmFja3NldCAqIHNpZ24oY29ybmVyLngpICogcGl4ZWxsZW5ndGgueC9waXhlbGxlbmd0aC55OwoJCQljb3ZlcmFnZSA9IChjb3ZlcmFnZSAtIC41KSAqIGFicyhjb3JuZXIueSkgLyAoYWJzKGNvcm5lci55KSArIGJhY2tzZXQpICsgLjU7CgkJfQoJfQoJZmxvYXQyeDIgc2tld21hdHJpeCA9IGZsb2F0MngyKHNrZXcueHksIHNrZXcuencpOwoJZmxvYXQyIGRldmNvb3JkID0gdmVydGV4cG9zICogc2tld21hdHJpeCArIHRyYW5zbGF0ZV9hbmRfbG9jYWxyb3RhdGUueHk7CglpZiAoMCAhPSBpc19saW5lYXJfY292ZXJhZ2UpIAoJewoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MigwLCBjb3ZlcmFnZSAqIGNvdmVyYWdlX211bHRpcGxpZXIpOwoJfQoJZWxzZSAKCXsKCQlmbG9hdDIgYXJjY29vcmQgPSAxIC0gYWJzKHJhZGl1c19vdXRzZXQpICsgYWFfb3V0c2V0L3JhZGlpICogY29ybmVyOwoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MihhcmNjb29yZC54KzEsIGFyY2Nvb3JkLnkpOwoJfQoJc2tfUG9zaXRpb24gPSBkZXZjb29yZC54eTAxOwp9CgAAAABFAgAAZmxhdCBpbiBoYWxmNCB2Y29sb3JfUzA7CmluIGZsb2F0MiB2YXJjY29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBGaWxsUlJlY3RPcDo6UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJZmxvYXQgeF9wbHVzXzE9dmFyY2Nvb3JkX1MwLngsIHk9dmFyY2Nvb3JkX1MwLnk7CgloYWxmIGNvdmVyYWdlOwoJaWYgKDAgPT0geF9wbHVzXzEpIAoJewoJCWNvdmVyYWdlID0gaGFsZih5KTsKCX0KCWVsc2UgCgl7CgkJZmxvYXQgZm4gPSB4X3BsdXNfMSAqICh4X3BsdXNfMSAtIDIpOwoJCWZuID0gZm1hKHkseSwgZm4pOwoJCWZsb2F0IGZud2lkdGggPSBmd2lkdGgoZm4pOwoJCWNvdmVyYWdlID0gLjUgLSBoYWxmKGZuL2Zud2lkdGgpOwoJCWNvdmVyYWdlID0gY2xhbXAoY292ZXJhZ2UsIDAsIDEpOwoJfQoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChjb3ZlcmFnZSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAACAAAAA4AAAByYWRpaV9zZWxlY3RvcgAAGQAAAGNvcm5lcl9hbmRfcmFkaXVzX291dHNldHMAAAAVAAAAYWFfYmxvYXRfYW5kX2NvdmVyYWdlAAAABwAAAHJhZGlpX3gABwAAAHJhZGlpX3kABAAAAHNrZXcZAAAAdHJhbnNsYXRlX2FuZF9sb2NhbHJvdGF0ZQAAAAUAAABjb2xvcgAAAAAAAAA=","HUIAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAAAAAALUAQMAAAAAAIAAEAAAABJYQAAAAAQAAIAAAAAWCBACAABAAAAANAECAZAAEAAAAAAAAFAAMAAAABAAAAAAABBAM":"CgAAAExTS1M2AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAAAAC8GAAB1bmlmb3JtIGZsb2F0NCB1Y2xhbXBfUzFfYzBfYzBfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMDsKdW5pZm9ybSBoYWxmMiB1SW5jcmVtZW50X1MxX2MwOwp1bmlmb3JtIGhhbGYyIHVPZmZzZXRzQW5kS2VybmVsX1MxX2MwWzEzXTsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxOwp1bmlmb3JtIHNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMTsKaW4gZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwpoYWxmNCBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwX2MwKGhhbGY0IF9pbnB1dCwgZmxvYXQyIF9jb29yZHMpIAp7CglmbG9hdDIgaW5Db29yZCA9IF9jb29yZHM7CglmbG9hdDIgc3Vic2V0Q29vcmQ7CglzdWJzZXRDb29yZC54ID0gaW5Db29yZC54OwoJc3Vic2V0Q29vcmQueSA9IGluQ29vcmQueTsKCWZsb2F0MiBjbGFtcGVkQ29vcmQ7CgljbGFtcGVkQ29vcmQueCA9IGNsYW1wKHN1YnNldENvb3JkLngsIHVjbGFtcF9TMV9jMF9jMF9jMC54LCB1Y2xhbXBfUzFfYzBfYzBfYzAueik7CgljbGFtcGVkQ29vcmQueSA9IHN1YnNldENvb3JkLnk7CgloYWxmNCB0ZXh0dXJlQ29sb3IgPSBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzEsIGNsYW1wZWRDb29yZCk7CglyZXR1cm4gdGV4dHVyZUNvbG9yOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMV9jMF9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBfY29vcmRzKSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzBfYzBfYzAoX2lucHV0LCBmbG9hdDN4Mih1bWF0cml4X1MxX2MwX2MwKSAqIF9jb29yZHMueHkxKTsKfQpoYWxmNCBTbW9vdGhfUzFfYzAoaGFsZjQgX2lucHV0LCBmbG9hdDIgY29vcmQsIGhhbGYyIG9mZnNldEFuZEtlcm5lbCkgCnsKCXJldHVybiBNYXRyaXhFZmZlY3RfUzFfYzBfYzAoX2lucHV0LCAoY29vcmQgKyBvZmZzZXRBbmRLZXJuZWwueCAqIHVJbmNyZW1lbnRfUzFfYzApKSAqIG9mZnNldEFuZEtlcm5lbC55Owp9CmhhbGY0IEdhdXNzaWFuQ29udm9sdXRpb25fUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgY29sb3IgPSBoYWxmNCgwKTsKCWZsb2F0MiBjb29yZCA9IHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwoJZm9yIChpbnQgaT0wOyBpPDEzOyArK2kpIAoJewoJCWNvbG9yICs9IFNtb290aF9TMV9jMChfaW5wdXQsIGNvb3JkLCB1T2Zmc2V0c0FuZEtlcm5lbF9TMV9jMFtpXSk7Cgl9CglyZXR1cm4gY29sb3I7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBHYXVzc2lhbkNvbnZvbHV0aW9uX1MxX2MwKF9pbnB1dCk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzAgPSBoYWxmNCgxKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBNYXRyaXhFZmZlY3RfUzEob3V0cHV0Q29sb3JfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgoAAABsb2NhbENvb3JkAAAAAAAA","B2AAQAAABQAAIAABBYAAB7777777777774ABICAAAAAAAAAAAAAABUABAAAAAEAAAAAIBEABAAAAA":"CgAAAExTS1MOAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmIGluQ292ZXJhZ2U7Cm91dCBoYWxmIHZpbkNvdmVyYWdlX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBEZWZhdWx0R2VvbWV0cnlQcm9jZXNzb3IKCWZsb2F0MiBfdG1wXzFfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247Cgl2aW5Db3ZlcmFnZV9TMCA9IGluQ292ZXJhZ2U7Cglza19Qb3NpdGlvbiA9IF90bXBfMV9pblBvc2l0aW9uLnh5MDE7Cn0KAAAAAAAATQEAAHVuaWZvcm0gaGFsZjQgdUNvbG9yX1MwOwppbiBoYWxmIHZpbkNvdmVyYWdlX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgRGVmYXVsdEdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdUNvbG9yX1MwOwoJaGFsZiBhbHBoYSA9IDEuMDsKCWFscGhhID0gdmluQ292ZXJhZ2VfUzA7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGFscGhhKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACgAAAGluUG9zaXRpb24AAAoAAABpbkNvdmVyYWdlAAAAAAAA","HUQAAAAAAAMAADAAAIOAAAH677776IZOCAAP577777777777777777YBAAAAAAAAAAADEAANAAAAALHCKLMRAAAAAAAAABAAAAAGJBCFLQVBWAQAAAAAAQAAAAAMACQCAACAAAAA2AIBAEIAAAAAAAAAAAAIADQAAAAIAAAAAAAIIDAAAAAA":"CgAAAExTS1PUAAAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmZsYXQgb3V0IGhhbGY0IHZjb2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKfQoBAAAAeAQAAHVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMDsKdW5pZm9ybSBoYWxmNCB1Y2lyY2xlRGF0YV9TMV9jMDsKdW5pZm9ybSBzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7CmZsYXQgaW4gaGFsZjQgdmNvbG9yX1MwOwpoYWxmNCBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwX2MwKGhhbGY0IF9pbnB1dCwgZmxvYXQyIF9jb29yZHMpIAp7CglyZXR1cm4gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MxLCBfY29vcmRzKS4wMDByOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMV9jMF9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBfY29vcmRzKSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzBfYzBfYzAoX2lucHV0LCBmbG9hdDN4Mih1bWF0cml4X1MxX2MwX2MwKSAqIF9jb29yZHMueHkxKTsKfQpoYWxmNCBDaXJjbGVCbHVyX1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJaGFsZjIgdmVjID0gaGFsZjIoKHNrX0ZyYWdDb29yZC54eSAtIGZsb2F0Mih1Y2lyY2xlRGF0YV9TMV9jMC54eSkpICogZmxvYXQodWNpcmNsZURhdGFfUzFfYzAudykpOwoJaGFsZiBkaXN0ID0gbGVuZ3RoKHZlYykgKyAoMC41IC0gdWNpcmNsZURhdGFfUzFfYzAueikgKiB1Y2lyY2xlRGF0YV9TMV9jMC53OwoJcmV0dXJuIGhhbGY0KE1hdHJpeEVmZmVjdF9TMV9jMF9jMChfdG1wXzBfaW5Db2xvciwgZmxvYXQyKGhhbGYyKGRpc3QsIDAuNSkpKS53d3d3KTsKfQpoYWxmNCBCbGVuZF9TMShoYWxmNCBfc3JjLCBoYWxmNCBfZHN0KSAKewoJcmV0dXJuIGJsZW5kX21vZHVsYXRlKENpcmNsZUJsdXJfUzFfYzAoX3NyYyksIF9zcmMpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gQmxlbmRfUzEob3V0cHV0Q292ZXJhZ2VfUzAsIGhhbGY0KDEpKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoBAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgUAAABjb2xvcgAAAAAAAAA=","HUJAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAQAAAAAAQQGACQAGAAAAAQAAAAAAAQQGAAA":"CgAAAExTS1PlAAAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7Cm91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmxvY2FsQ29vcmRfUzAgPSBsb2NhbENvb3JkOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAAAAAACfAQAAdW5pZm9ybSBzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzA7CmluIGZsb2F0MiB2bG9jYWxDb29yZF9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzAgPSBoYWxmNCgxKTsKCWZsb2F0MiB0ZXhDb29yZDsKCXRleENvb3JkID0gdmxvY2FsQ29vcmRfUzA7CglvdXRwdXRDb2xvcl9TMCA9ICgoc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB0ZXhDb29yZCkgKiBoYWxmNCgxKSkpOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgoAAABsb2NhbENvb3JkAAAAAAAA","AYTRVAADQAAAOAEARAFQJAABBADAAAILBYAACCYUQD777777777767YAAAAAAAAAAAAOQAAAAAAAQAAAABAMQAAAAA":"CgAAAExTS1NyAgAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0NCBpbkNpcmNsZUVkZ2U7CmluIGhhbGYzIGluQ2xpcFBsYW5lOwppbiBoYWxmMyBpbklzZWN0UGxhbmU7Cm91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKb3V0IGhhbGYzIHZpbkNsaXBQbGFuZV9TMDsKb3V0IGhhbGYzIHZpbklzZWN0UGxhbmVfUzA7Cm91dCBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgQ2lyY2xlR2VvbWV0cnlQcm9jZXNzb3IKCXZpbkNpcmNsZUVkZ2VfUzAgPSBpbkNpcmNsZUVkZ2U7Cgl2aW5DbGlwUGxhbmVfUzAgPSBpbkNsaXBQbGFuZTsKCXZpbklzZWN0UGxhbmVfUzAgPSBpbklzZWN0UGxhbmU7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8wX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJZmxvYXQyIF90bXBfMV9pblBvc2l0aW9uID0gdWxvY2FsTWF0cml4X1MwLnh6ICogaW5Qb3NpdGlvbiArIHVsb2NhbE1hdHJpeF9TMC55dzsKCXNrX1Bvc2l0aW9uID0gX3RtcF8wX2luUG9zaXRpb24ueHkwMTsKfQoAAAAAAADdAwAAaW4gZmxvYXQ0IHZpbkNpcmNsZUVkZ2VfUzA7CmluIGhhbGYzIHZpbkNsaXBQbGFuZV9TMDsKaW4gaGFsZjMgdmluSXNlY3RQbGFuZV9TMDsKaW4gaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQ0IGNpcmNsZUVkZ2U7CgljaXJjbGVFZGdlID0gdmluQ2lyY2xlRWRnZV9TMDsKCWhhbGYzIGNsaXBQbGFuZTsKCWNsaXBQbGFuZSA9IHZpbkNsaXBQbGFuZV9TMDsKCWhhbGYzIGlzZWN0UGxhbmU7Cglpc2VjdFBsYW5lID0gdmluSXNlY3RQbGFuZV9TMDsKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWZsb2F0IGQgPSBsZW5ndGgoY2lyY2xlRWRnZS54eSk7CgloYWxmIGRpc3RhbmNlVG9PdXRlckVkZ2UgPSBoYWxmKGNpcmNsZUVkZ2UueiAqICgxLjAgLSBkKSk7CgloYWxmIGVkZ2VBbHBoYSA9IHNhdHVyYXRlKGRpc3RhbmNlVG9PdXRlckVkZ2UpOwoJaGFsZiBkaXN0YW5jZVRvSW5uZXJFZGdlID0gaGFsZihjaXJjbGVFZGdlLnogKiAoZCAtIGNpcmNsZUVkZ2UudykpOwoJaGFsZiBpbm5lckFscGhhID0gc2F0dXJhdGUoZGlzdGFuY2VUb0lubmVyRWRnZSk7CgllZGdlQWxwaGEgKj0gaW5uZXJBbHBoYTsKCWhhbGYgY2xpcCA9IGhhbGYoc2F0dXJhdGUoY2lyY2xlRWRnZS56ICogZG90KGNpcmNsZUVkZ2UueHksIGNsaXBQbGFuZS54eSkgKyBjbGlwUGxhbmUueikpOwoJY2xpcCAqPSBoYWxmKHNhdHVyYXRlKGNpcmNsZUVkZ2UueiAqIGRvdChjaXJjbGVFZGdlLnh5LCBpc2VjdFBsYW5lLnh5KSArIGlzZWN0UGxhbmUueikpOwoJZWRnZUFscGhhICo9IGNsaXA7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGVkZ2VBbHBoYSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAoAAABpblBvc2l0aW9uAAAHAAAAaW5Db2xvcgAMAAAAaW5DaXJjbGVFZGdlCwAAAGluQ2xpcFBsYW5lAAwAAABpbklzZWN0UGxhbmUAAAAA","HUJAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAQAAAAAAQQGAAZAADIAAAACU53QJEKAAAAAAMAAAAAIAAAAAAGIRDFB2XASAUAABQAAAAAAAAAAAAADUAAAAAAAEAAAAAIDEAAA":"CgAAAExTS1PlAAAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7Cm91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmxvY2FsQ29vcmRfUzAgPSBsb2NhbENvb3JkOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAAEAAACrBAAAY29uc3QgaW50IGtGaWxsQUFfUzFfYzAgPSAxOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQldfUzFfYzAgPSAyOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQUFfUzFfYzAgPSAzOwp1bmlmb3JtIGZsb2F0NCB1Y2lyY2xlX1MxX2MwOwp1bmlmb3JtIHNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMDsKaW4gZmxvYXQyIHZsb2NhbENvb3JkX1MwOwpoYWxmNCBDaXJjbGVfUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF8wX2luQ29sb3IgPSBfaW5wdXQ7CgloYWxmIGQ7CglpZiAoaW50KDEpID09IGtJbnZlcnNlRmlsbEJXX1MxX2MwIHx8IGludCgxKSA9PSBrSW52ZXJzZUZpbGxBQV9TMV9jMCkgCgl7CgkJZCA9IGhhbGYoKGxlbmd0aCgodWNpcmNsZV9TMV9jMC54eSAtIHNrX0ZyYWdDb29yZC54eSkgKiB1Y2lyY2xlX1MxX2MwLncpIC0gMS4wKSAqIHVjaXJjbGVfUzFfYzAueik7Cgl9CgllbHNlIAoJewoJCWQgPSBoYWxmKCgxLjAgLSBsZW5ndGgoKHVjaXJjbGVfUzFfYzAueHkgLSBza19GcmFnQ29vcmQueHkpICogdWNpcmNsZV9TMV9jMC53KSkgKiB1Y2lyY2xlX1MxX2MwLnopOwoJfQoJcmV0dXJuIGhhbGY0KGhhbGY0KGludCgxKSA9PSBrRmlsbEFBX1MxX2MwIHx8IGludCgxKSA9PSBrSW52ZXJzZUZpbGxBQV9TMV9jMCA/IHNhdHVyYXRlKGQpIDogaGFsZihkID4gMC41ID8gMSA6IDApKSk7Cn0KaGFsZjQgQmxlbmRfUzEoaGFsZjQgX3NyYywgaGFsZjQgX2RzdCkgCnsKCXJldHVybiBibGVuZF9tb2R1bGF0ZShfc3JjLCBDaXJjbGVfUzFfYzAoX3NyYykpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwID0gaGFsZjQoMSk7CglmbG9hdDIgdGV4Q29vcmQ7Cgl0ZXhDb29yZCA9IHZsb2NhbENvb3JkX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSAoKHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMCwgdGV4Q29vcmQpICogaGFsZjQoMSkpKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBCbGVuZF9TMShvdXRwdXRDb3ZlcmFnZV9TMCwgaGFsZjQoMSkpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0X1MxOwoJfQp9CgABAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgoAAABsb2NhbENvb3JkAAAAAAAA","HUQAAAAAAAMAADAAAIOAAAH677776IZOCAAP577777777777777777YBAAAAAAAAAAAHEADZAAAAAAIAAAAAAOQAAAAAAAQAAAABAMQAAAAAA":"CgAAAExTS1PUAAAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmZsYXQgb3V0IGhhbGY0IHZjb2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKfQoBAAAAoAIAAHVuaWZvcm0gZmxvYXQ0IHVpbm5lclJlY3RfUzE7CnVuaWZvcm0gaGFsZjIgdXJhZGl1c1BsdXNIYWxmX1MxOwpmbGF0IGluIGhhbGY0IHZjb2xvcl9TMDsKaGFsZjQgQ2lyY3VsYXJSUmVjdF9TMShoYWxmNCBfaW5wdXQpIAp7CglmbG9hdDIgZHh5MCA9IHVpbm5lclJlY3RfUzEuTFQgLSBza19GcmFnQ29vcmQueHk7CglmbG9hdDIgZHh5MSA9IHNrX0ZyYWdDb29yZC54eSAtIHVpbm5lclJlY3RfUzEuUkI7CglmbG9hdDIgZHh5ID0gbWF4KG1heChkeHkwLCBkeHkxKSwgMC4wKTsKCWhhbGYgYWxwaGEgPSBoYWxmKHNhdHVyYXRlKHVyYWRpdXNQbHVzSGFsZl9TMS54IC0gbGVuZ3RoKGR4eSkpKTsKCXJldHVybiBfaW5wdXQgKiBhbHBoYTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IENpcmN1bGFyUlJlY3RfUzEob3V0cHV0Q292ZXJhZ2VfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0X1MxOwoJfQp9CgEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAAAAAAAA==","HUJAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAQAAAAAAQQGABZAA6IAAAAACAAAAAADUAAAAAAAEAAAAAIDEAAAAAAA":"CgAAAExTS1PlAAAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7Cm91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmxvY2FsQ29vcmRfUzAgPSBsb2NhbENvb3JkOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAAEAAAAuAwAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CnVuaWZvcm0gc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MwOwppbiBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CmhhbGY0IENpcmN1bGFyUlJlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MxLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MxLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CgloYWxmIGFscGhhID0gaGFsZihzYXR1cmF0ZSh1cmFkaXVzUGx1c0hhbGZfUzEueCAtIGxlbmd0aChkeHkpKSk7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzAgPSBoYWxmNCgxKTsKCWZsb2F0MiB0ZXhDb29yZDsKCXRleENvb3JkID0gdmxvY2FsQ29vcmRfUzA7CglvdXRwdXRDb2xvcl9TMCA9ICgoc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB0ZXhDb29yZCkgKiBoYWxmNCgxKSkpOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IENpcmN1bGFyUlJlY3RfUzEob3V0cHV0Q292ZXJhZ2VfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0X1MxOwoJfQp9CgAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAIAAAAcG9zaXRpb24KAAAAbG9jYWxDb29yZAAAAAAAAA==","EADQAAAAAEAAAAAUAABQAAQPAAABCFYMAAKAUEAAAAAAAAABAAAAAAAAAAANAAIAAAABAAAAACAJAAIAAAAA":"CgAAAExTS1NyAgAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQyIHVBdGxhc0RpbWVuc2lvbnNJbnZfUzA7CmluIGZsb2F0MyBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiB1c2hvcnQyIGluVGV4dHVyZUNvb3JkczsKb3V0IGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKZmxhdCBvdXQgZmxvYXQgdlRleEluZGV4X1MwOwpvdXQgZmxvYXQyIHZJbnRUZXh0dXJlQ29vcmRzX1MwOwpvdXQgaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIERpc3RhbmNlRmllbGRQYXRoCglpbnQgdGV4SWR4ID0gMDsKCWZsb2F0MiB1bm9ybVRleENvb3JkcyA9IGZsb2F0MihpblRleHR1cmVDb29yZHMueCwgaW5UZXh0dXJlQ29vcmRzLnkpOwoJdlRleHR1cmVDb29yZHNfUzAgPSB1bm9ybVRleENvb3JkcyAqIHVBdGxhc0RpbWVuc2lvbnNJbnZfUzA7Cgl2VGV4SW5kZXhfUzAgPSBmbG9hdCh0ZXhJZHgpOwoJdkludFRleHR1cmVDb29yZHNfUzAgPSB1bm9ybVRleENvb3JkczsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MyBfdG1wXzFfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247Cglza19Qb3NpdGlvbiA9IGluUG9zaXRpb24ueHkwejsKfQoAAAAAAACfAgAAdW5pZm9ybSBzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzA7CmluIGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKZmxhdCBpbiBmbG9hdCB2VGV4SW5kZXhfUzA7CmluIGZsb2F0MiB2SW50VGV4dHVyZUNvb3Jkc19TMDsKaW4gaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBEaXN0YW5jZUZpZWxkUGF0aAoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZpbkNvbG9yX1MwOwoJZmxvYXQyIHV2ID0gdlRleHR1cmVDb29yZHNfUzA7CgloYWxmNCB0ZXhDb2xvcjsKCXsKCQl0ZXhDb2xvciA9IHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMCwgdXYpLnJycnI7Cgl9CgloYWxmIGRpc3RhbmNlID0gNy45Njg3NSoodGV4Q29sb3IuciAtIDAuNTAxOTYwNzg0MzEpOwoJaGFsZiBhZndpZHRoOwoJYWZ3aWR0aCA9IGFicygwLjY1KmhhbGYoZEZkeCh2SW50VGV4dHVyZUNvb3Jkc19TMC54KSkpOwoJaGFsZiB2YWwgPSBzbW9vdGhzdGVwKC1hZndpZHRoLCBhZndpZHRoLCBkaXN0YW5jZSk7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KHZhbCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADwAAAGluVGV4dHVyZUNvb3JkcwAAAAAA","AYQA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAAAAB3QA6AAAEAAAAAAAMAAPEAEAAABAAAAAAB2AAAAAAACAAAAAEBSAAAA":"CgAAAExTS1PMAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0NCBpbkNpcmNsZUVkZ2U7Cm91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJdmluQ2lyY2xlRWRnZV9TMCA9IGluQ2lyY2xlRWRnZTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSB1bG9jYWxNYXRyaXhfUzAueHogKiBpblBvc2l0aW9uICsgdWxvY2FsTWF0cml4X1MwLnl3OwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgEAAAAeBQAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CnVuaWZvcm0gZmxvYXQ0IHVpbm5lclJlY3RfUzI7CnVuaWZvcm0gaGFsZjIgdXJhZGl1c1BsdXNIYWxmX1MyOwppbiBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKaW4gaGFsZjQgdmluQ29sb3JfUzA7CmhhbGY0IENpcmN1bGFyUlJlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MxLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MxLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CgloYWxmIGFscGhhID0gaGFsZihzYXR1cmF0ZSh1cmFkaXVzUGx1c0hhbGZfUzEueCAtIGxlbmd0aChkeHkpKSk7CglhbHBoYSA9IDEuMCAtIGFscGhhOwoJcmV0dXJuIF9pbnB1dCAqIGFscGhhOwp9CmhhbGY0IENpcmN1bGFyUlJlY3RfUzIoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MyLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MyLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CgloYWxmIGFscGhhID0gaGFsZihzYXR1cmF0ZSh1cmFkaXVzUGx1c0hhbGZfUzIueCAtIGxlbmd0aChkeHkpKSk7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIENpcmNsZUdlb21ldHJ5UHJvY2Vzc29yCglmbG9hdDQgY2lyY2xlRWRnZTsKCWNpcmNsZUVkZ2UgPSB2aW5DaXJjbGVFZGdlX1MwOwoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZpbkNvbG9yX1MwOwoJZmxvYXQgZCA9IGxlbmd0aChjaXJjbGVFZGdlLnh5KTsKCWhhbGYgZGlzdGFuY2VUb091dGVyRWRnZSA9IGhhbGYoY2lyY2xlRWRnZS56ICogKDEuMCAtIGQpKTsKCWhhbGYgZWRnZUFscGhhID0gc2F0dXJhdGUoZGlzdGFuY2VUb091dGVyRWRnZSk7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGVkZ2VBbHBoYSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBDaXJjdWxhclJSZWN0X1MxKG91dHB1dENvdmVyYWdlX1MwKTsKCWhhbGY0IG91dHB1dF9TMjsKCW91dHB1dF9TMiA9IENpcmN1bGFyUlJlY3RfUzIob3V0cHV0X1MxKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMjsKCX0KfQoAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACgAAAGluUG9zaXRpb24AAAcAAABpbkNvbG9yAAwAAABpbkNpcmNsZUVkZ2UAAAAA","HTQAAGAABBYAAAEIXBAAAGEAMAAAAAAAAAAAAAAAQAHAAAAAQAAAAAAAQQGAAAAA":"CgAAAExTS1M/AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiBmbG9hdDQgaW5RdWFkRWRnZTsKb3V0IGZsb2F0NCB2UXVhZEVkZ2VfUzA7Cm91dCBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZEVkZ2UKCXZRdWFkRWRnZV9TMCA9IGluUXVhZEVkZ2U7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8wX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgABAAAABQMAAGluIGZsb2F0NCB2UXVhZEVkZ2VfUzA7CmluIGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZEVkZ2UKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWhhbGYgZWRnZUFscGhhOwoJaGFsZjIgZHV2ZHggPSBoYWxmMihkRmR4KHZRdWFkRWRnZV9TMC54eSkpOwoJaGFsZjIgZHV2ZHkgPSBoYWxmMihkRmR5KHZRdWFkRWRnZV9TMC54eSkpOwoJaWYgKHZRdWFkRWRnZV9TMC56ID4gMC4wICYmIHZRdWFkRWRnZV9TMC53ID4gMC4wKSAKCXsKCQllZGdlQWxwaGEgPSBoYWxmKG1pbihtaW4odlF1YWRFZGdlX1MwLnosIHZRdWFkRWRnZV9TMC53KSArIDAuNSwgMS4wKSk7Cgl9CgllbHNlIAoJewoJCWhhbGYyIGdGID0gaGFsZjIoaGFsZigyLjAqdlF1YWRFZGdlX1MwLngqZHV2ZHgueCAtIGR1dmR4LnkpLCAgICAgICAgICAgICAgICAgaGFsZigyLjAqdlF1YWRFZGdlX1MwLngqZHV2ZHkueCAtIGR1dmR5LnkpKTsKCQllZGdlQWxwaGEgPSBoYWxmKHZRdWFkRWRnZV9TMC54KnZRdWFkRWRnZV9TMC54IC0gdlF1YWRFZGdlX1MwLnkpOwoJCWVkZ2VBbHBoYSA9IHNhdHVyYXRlKDAuNSAtIGVkZ2VBbHBoYSAvIGxlbmd0aChnRikpOwoJfQoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChlZGdlQWxwaGEpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IACgAAAGluUXVhZEVkZ2UAAAAAAAA=","GEMAAAYAAEHAAAARC4EAAAQWBQAAAAAAAAAQAAAAIBCAAAGQAEAAAAAQAAAABAEQAEAAAAA":"CgAAAExTS1NUAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiBoYWxmMyBpblNoYWRvd1BhcmFtczsKb3V0IGhhbGYzIHZpblNoYWRvd1BhcmFtc19TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBSUmVjdFNoYWRvdwoJdmluU2hhZG93UGFyYW1zX1MwID0gaW5TaGFkb3dQYXJhbXM7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8wX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgAAAAALAgAAdW5pZm9ybSBzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzA7CmluIGhhbGYzIHZpblNoYWRvd1BhcmFtc19TMDsKaW4gaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBSUmVjdFNoYWRvdwoJaGFsZjMgc2hhZG93UGFyYW1zOwoJc2hhZG93UGFyYW1zID0gdmluU2hhZG93UGFyYW1zX1MwOwoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZpbkNvbG9yX1MwOwoJaGFsZiBkID0gbGVuZ3RoKHNoYWRvd1BhcmFtcy54eSk7CglmbG9hdDIgdXYgPSBmbG9hdDIoc2hhZG93UGFyYW1zLnogKiAoMS4wIC0gZCksIDAuNSk7CgloYWxmIGZhY3RvciA9IHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMCwgdXYpLjAwMHIuYTsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoZmFjdG9yKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAoAAABpblBvc2l0aW9uAAAHAAAAaW5Db2xvcgAOAAAAaW5TaGFkb3dQYXJhbXMAAAAAAAA=","BYIBQAAABQAAIAABBYAAAEIXBAAP777777777777AAAAAAAAAAAABUABAAAAAEAAAAAIBEABAAAAA":"CgAAAExTS1M+AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwpvdXQgaGFsZjQgdmNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBEZWZhdWx0R2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IGNvbG9yID0gaW5Db2xvcjsKCXZjb2xvcl9TMCA9IGNvbG9yOwoJZmxvYXQyIF90bXBfMV9pblBvc2l0aW9uID0gaW5Qb3NpdGlvbjsKCWZsb2F0MiBfdG1wXzNfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247Cglza19Qb3NpdGlvbiA9IF90bXBfMV9pblBvc2l0aW9uLnh5MDE7Cn0KAAAAAAAABgEAAGluIGhhbGY0IHZjb2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIERlZmF1bHRHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACgAAAGluUG9zaXRpb24AAAcAAABpbkNvbG9yAAAAAAA=","HWQACAAAABAAADAAAIOAAAAADIIAAIRODAAP577774DSAIAA737777YBAAAAAAAAAAAHEADZAAAAAAIAAAAAAOQAAAAAAAQAAAABAMQAAAAAA":"CgAAAExTS1ONAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQgY292ZXJhZ2U7CmluIGhhbGY0IGNvbG9yOwppbiBmbG9hdDQgZ2VvbVN1YnNldDsKZmxhdCBvdXQgaGFsZjQgdmNvbG9yX1MwOwpvdXQgZmxvYXQgdmNvdmVyYWdlX1MwOwpmbGF0IG91dCBmbG9hdDQgdmdlb21TdWJzZXRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQyIHBvc2l0aW9uID0gcG9zaXRpb24ueHk7Cgl2Y29sb3JfUzAgPSBjb2xvcjsKCXZjb3ZlcmFnZV9TMCA9IGNvdmVyYWdlOwoJdmdlb21TdWJzZXRfUzAgPSBnZW9tU3Vic2V0OwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAAEAAAAIBAAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CmZsYXQgaW4gaGFsZjQgdmNvbG9yX1MwOwppbiBmbG9hdCB2Y292ZXJhZ2VfUzA7CmZsYXQgaW4gZmxvYXQ0IHZnZW9tU3Vic2V0X1MwOwpoYWxmNCBDaXJjdWxhclJSZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCWZsb2F0MiBkeHkwID0gdWlubmVyUmVjdF9TMS5MVCAtIHNrX0ZyYWdDb29yZC54eTsKCWZsb2F0MiBkeHkxID0gc2tfRnJhZ0Nvb3JkLnh5IC0gdWlubmVyUmVjdF9TMS5SQjsKCWZsb2F0MiBkeHkgPSBtYXgobWF4KGR4eTAsIGR4eTEpLCAwLjApOwoJaGFsZiBhbHBoYSA9IGhhbGYoc2F0dXJhdGUodXJhZGl1c1BsdXNIYWxmX1MxLnggLSBsZW5ndGgoZHh5KSkpOwoJcmV0dXJuIF9pbnB1dCAqIGFscGhhOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7CglmbG9hdCBjb3ZlcmFnZSA9IHZjb3ZlcmFnZV9TMDsKCWZsb2F0NCBnZW9TdWJzZXQ7CglnZW9TdWJzZXQgPSB2Z2VvbVN1YnNldF9TMDsKCWhhbGY0IGRpc3RzNCA9IGNsYW1wKGhhbGY0KDEsIDEsIC0xLCAtMSkgKiBoYWxmNChza19GcmFnQ29vcmQueHl4eSAtIGdlb1N1YnNldCksIDAsIDEpOwoJaGFsZjIgZGlzdHMyID0gZGlzdHM0Lnh5ICsgZGlzdHM0Lnp3IC0gMTsKCWhhbGYgc3Vic2V0Q292ZXJhZ2UgPSBkaXN0czIueCAqIGRpc3RzMi55OwoJY292ZXJhZ2UgPSBtaW4oY292ZXJhZ2UsIHN1YnNldENvdmVyYWdlKTsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoaGFsZihjb3ZlcmFnZSkpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gQ2lyY3VsYXJSUmVjdF9TMShvdXRwdXRDb3ZlcmFnZV9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzE7Cgl9Cn0KAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAIAAAAcG9zaXRpb24IAAAAY292ZXJhZ2UFAAAAY29sb3IAAAAKAAAAZ2VvbVN1YnNldAAAAAAAAA==","HVIACAAAABQAAGAAAQ4AAAAAGQQAARC4GAAAIOCAAD6P7777777777YDAAAAAAAAAAAFIBQU7BTXIAAAAAACAAAAAAQFV5W6JEAAAAAYAAAABQEZ2AKAWAQAABAL6SYKDYAAAACAAAAAAQEGIAAAAACAWTWL3EYAAAADAAAAACADHIJJCYCAAAEAP2LRIPAAAAAIAAAAAAABTALI3F5SOAIAABQAAAAAABTUEUZMBAAAAAH5FYUXQAAAAAAAEAAAAAZMRGOQCQFQEAAAAAAAAAGARL2LXJHAAEAAAAAEAAAABSCQX5FQUHQAAAAAAAAAACAA4AAAABAACAAAACCAYAAAAA":"CgAAAExTS1PrAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMF9jMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdCBjb3ZlcmFnZTsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpmbGF0IG91dCBoYWxmNCB2Y29sb3JfUzA7Cm91dCBmbG9hdCB2Y292ZXJhZ2VfUzA7Cm91dCBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzZfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQyIHBvc2l0aW9uID0gcG9zaXRpb24ueHk7Cgl2Y29sb3JfUzAgPSBjb2xvcjsKCXZjb3ZlcmFnZV9TMCA9IGNvdmVyYWdlOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwoJewoJCXZUcmFuc2Zvcm1lZENvb3Jkc182X1MwID0gZmxvYXQzeDIodW1hdHJpeF9TMV9jMF9jMF9jMSkgKiBsb2NhbENvb3JkLnh5MTsKCX0KfQoAAAAAAMMHAAB1bmlmb3JtIGhhbGY0IHVzdGFydF9TMV9jMF9jMF9jMDsKdW5pZm9ybSBoYWxmNCB1ZW5kX1MxX2MwX2MwX2MwOwp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzBfYzBfYzE7CnVuaWZvcm0gaGFsZjQgdWxlZnRCb3JkZXJDb2xvcl9TMV9jMF9jMDsKdW5pZm9ybSBoYWxmNCB1cmlnaHRCb3JkZXJDb2xvcl9TMV9jMF9jMDsKZmxhdCBpbiBoYWxmNCB2Y29sb3JfUzA7CmluIGZsb2F0IHZjb3ZlcmFnZV9TMDsKaW4gZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc182X1MwOwpoYWxmNCBTaW5nbGVJbnRlcnZhbENvbG9yaXplcl9TMV9jMF9jMF9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBfY29vcmRzKSAKewoJaGFsZjQgX3RtcF8wX2luQ29sb3IgPSBfaW5wdXQ7CglmbG9hdDIgX3RtcF8xX2Nvb3JkcyA9IF9jb29yZHM7CglyZXR1cm4gaGFsZjQobWl4KHVzdGFydF9TMV9jMF9jMF9jMCwgdWVuZF9TMV9jMF9jMF9jMCwgaGFsZihfdG1wXzFfY29vcmRzLngpKSk7Cn0KaGFsZjQgTGluZWFyTGF5b3V0X1MxX2MwX2MwX2MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMl9pbkNvbG9yID0gX2lucHV0OwoJZmxvYXQyIF90bXBfM19jb29yZHMgPSB2VHJhbnNmb3JtZWRDb29yZHNfNl9TMDsKCXJldHVybiBoYWxmNChoYWxmNChoYWxmKF90bXBfM19jb29yZHMueCkgKyAxZS0wNSwgMS4wLCAwLjAsIDAuMCkpOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMV9jMF9jMF9jMShoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gTGluZWFyTGF5b3V0X1MxX2MwX2MwX2MxX2MwKF9pbnB1dCk7Cn0KaGFsZjQgQ2xhbXBlZEdyYWRpZW50X1MxX2MwX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfNF9pbkNvbG9yID0gX2lucHV0OwoJaGFsZjQgdCA9IE1hdHJpeEVmZmVjdF9TMV9jMF9jMF9jMShfdG1wXzRfaW5Db2xvcik7CgloYWxmNCBvdXRDb2xvcjsKCWlmICghYm9vbChpbnQoMSkpICYmIHQueSA8IDAuMCkgCgl7CgkJb3V0Q29sb3IgPSBoYWxmNCgwLjApOwoJfQoJZWxzZSBpZiAodC54IDwgMC4wKSAKCXsKCQlvdXRDb2xvciA9IHVsZWZ0Qm9yZGVyQ29sb3JfUzFfYzBfYzA7Cgl9CgllbHNlIGlmICh0LnggPiAxLjApIAoJewoJCW91dENvbG9yID0gdXJpZ2h0Qm9yZGVyQ29sb3JfUzFfYzBfYzA7Cgl9CgllbHNlIAoJewoJCW91dENvbG9yID0gU2luZ2xlSW50ZXJ2YWxDb2xvcml6ZXJfUzFfYzBfYzBfYzAoX3RtcF80X2luQ29sb3IsIGZsb2F0MihoYWxmMih0LngsIDAuMCkpKTsKCX0KCXJldHVybiBoYWxmNChvdXRDb2xvcik7Cn0KaGFsZjQgY29sb3JfeGZvcm1fUzFfYzAoZmxvYXQ0IGNvbG9yKSAKewoJY29sb3IucmdiICo9IGNvbG9yLmE7CglyZXR1cm4gaGFsZjQoY29sb3IpOwp9CmhhbGY0IENvbG9yU3BhY2VYZm9ybV9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gY29sb3JfeGZvcm1fUzFfYzAoQ2xhbXBlZEdyYWRpZW50X1MxX2MwX2MwKF9pbnB1dCkpOwp9CmhhbGY0IERpc2FibGVDb3ZlcmFnZUFzQWxwaGFfUzEoaGFsZjQgX2lucHV0KSAKewoJX2lucHV0ID0gQ29sb3JTcGFjZVhmb3JtX1MxX2MwKF9pbnB1dCk7CgloYWxmNCBfdG1wXzVfaW5Db2xvciA9IF9pbnB1dDsKCXJldHVybiBoYWxmNChfaW5wdXQpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7CglmbG9hdCBjb3ZlcmFnZSA9IHZjb3ZlcmFnZV9TMDsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoaGFsZihjb3ZlcmFnZSkpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gRGlzYWJsZUNvdmVyYWdlQXNBbHBoYV9TMShvdXRwdXRDb2xvcl9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gKGhhbGY0KDEuMCkgLSBvdXRwdXRfUzEpICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAHBvc2l0aW9uCAAAAGNvdmVyYWdlBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","AYAA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAAAAOQAAAAAAAQAAAABAMQAAAAA":"CgAAAExTS1OCAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiBmbG9hdDQgaW5DaXJjbGVFZGdlOwpvdXQgZmxvYXQ0IHZpbkNpcmNsZUVkZ2VfUzA7Cm91dCBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgQ2lyY2xlR2VvbWV0cnlQcm9jZXNzb3IKCXZpbkNpcmNsZUVkZ2VfUzAgPSBpbkNpcmNsZUVkZ2U7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8wX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJZmxvYXQyIF90bXBfMl9pblBvc2l0aW9uID0gaW5Qb3NpdGlvbjsKCXNrX1Bvc2l0aW9uID0gX3RtcF8wX2luUG9zaXRpb24ueHkwMTsKfQoAAAAAAADqAQAAaW4gZmxvYXQ0IHZpbkNpcmNsZUVkZ2VfUzA7CmluIGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgQ2lyY2xlR2VvbWV0cnlQcm9jZXNzb3IKCWZsb2F0NCBjaXJjbGVFZGdlOwoJY2lyY2xlRWRnZSA9IHZpbkNpcmNsZUVkZ2VfUzA7CgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmluQ29sb3JfUzA7CglmbG9hdCBkID0gbGVuZ3RoKGNpcmNsZUVkZ2UueHkpOwoJaGFsZiBkaXN0YW5jZVRvT3V0ZXJFZGdlID0gaGFsZihjaXJjbGVFZGdlLnogKiAoMS4wIC0gZCkpOwoJaGFsZiBlZGdlQWxwaGEgPSBzYXR1cmF0ZShkaXN0YW5jZVRvT3V0ZXJFZGdlKTsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoZWRnZUFscGhhKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADAAAAGluQ2lyY2xlRWRnZQAAAAA=","HVIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAFQBQU7BTXIAAAAAAAAAACAAAAAVQEAAQAAAAAQCDAEQQGAAAAAAAAAAAA4IAPAAACAAAAAAAEABYAAAAEAAAAAAAEEBQA":"CgAAAExTS1N6AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMDsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBoYWxmNCBjb2xvcjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7CmZsYXQgb3V0IGhhbGY0IHZjb2xvcl9TMDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfM19TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzFfYzApICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAABAAAArAQAAHVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMDsKdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMjsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzI7CnVuaWZvcm0gc2FtcGxlckV4dGVybmFsT0VTIHVUZXh0dXJlU2FtcGxlcl8wX1MxOwpmbGF0IGluIGhhbGY0IHZjb2xvcl9TMDsKaW4gZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc18zX1MwOwpoYWxmNCBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzEsIHZUcmFuc2Zvcm1lZENvb3Jkc18zX1MwKTsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzBfYzAoX2lucHV0KTsKfQpoYWxmNCBEaXNhYmxlQ292ZXJhZ2VBc0FscGhhX1MxKGhhbGY0IF9pbnB1dCkgCnsKCV9pbnB1dCA9IE1hdHJpeEVmZmVjdF9TMV9jMChfaW5wdXQpOwoJaGFsZjQgX3RtcF8wX2luQ29sb3IgPSBfaW5wdXQ7CglyZXR1cm4gaGFsZjQoX2lucHV0KTsKfQpoYWxmNCBDaXJjdWxhclJSZWN0X1MyKGhhbGY0IF9pbnB1dCkgCnsKCWZsb2F0MiBkeHkwID0gdWlubmVyUmVjdF9TMi5MVCAtIHNrX0ZyYWdDb29yZC54eTsKCWZsb2F0MiBkeHkxID0gc2tfRnJhZ0Nvb3JkLnh5IC0gdWlubmVyUmVjdF9TMi5SQjsKCWZsb2F0MiBkeHkgPSBtYXgobWF4KGR4eTAsIGR4eTEpLCAwLjApOwoJaGFsZiBhbHBoYSA9IGhhbGYoc2F0dXJhdGUodXJhZGl1c1BsdXNIYWxmX1MyLnggLSBsZW5ndGgoZHh5KSkpOwoJcmV0dXJuIF9pbnB1dCAqIGFscGhhOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gRGlzYWJsZUNvdmVyYWdlQXNBbHBoYV9TMShvdXRwdXRDb2xvcl9TMCk7CgloYWxmNCBvdXRwdXRfUzI7CglvdXRwdXRfUzIgPSBDaXJjdWxhclJSZWN0X1MyKG91dHB1dENvdmVyYWdlX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRfUzEgKiBvdXRwdXRfUzI7Cgl9Cn0KAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAIAAAAcG9zaXRpb24FAAAAY29sb3IAAAAKAAAAbG9jYWxDb29yZAAAAAAAAA==","B2ABSAAABQAAIAABBYAAB7777777777774ABICAAAAAAAAAAAAAABUABAAAAAEAAAAAIBEABAAAAA":"CgAAAExTS1N4AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gaGFsZjQgdUNvbG9yX1MwOwppbiBmbG9hdDIgaW5Qb3NpdGlvbjsKaW4gaGFsZiBpbkNvdmVyYWdlOwpvdXQgaGFsZjQgdmNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBEZWZhdWx0R2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IGNvbG9yID0gdUNvbG9yX1MwOwoJY29sb3IgPSBjb2xvciAqIGluQ292ZXJhZ2U7Cgl2Y29sb3JfUzAgPSBjb2xvcjsKCWZsb2F0MiBfdG1wXzFfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8zX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJc2tfUG9zaXRpb24gPSBfdG1wXzFfaW5Qb3NpdGlvbi54eTAxOwp9CgAAAAAGAQAAaW4gaGFsZjQgdmNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgRGVmYXVsdEdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAKAAAAaW5Qb3NpdGlvbgAACgAAAGluQ292ZXJhZ2UAAAAAAAA=","DAQAAAAAAABGAABAYAAQAIHCAIAYAQUBAEAAAAAAEAAAAAAAAAAAAIAD2AAAAAAQAVSWGRIBAAADAAAAACAAAAAAQCGEIQOZLBIQAAAABQAAAAAAAAAAAAFAAMAAAABAAAAAAABBAMAAA":"CgAAAExTS1MWAgAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQyIHVBdGxhc1NpemVJbnZfUzA7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiB1c2hvcnQyIGluVGV4dHVyZUNvb3JkczsKb3V0IGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKZmxhdCBvdXQgZmxvYXQgdlRleEluZGV4X1MwOwpvdXQgaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIEJpdG1hcFRleHQKCWludCB0ZXhJZHggPSAwOwoJZmxvYXQyIHVub3JtVGV4Q29vcmRzID0gZmxvYXQyKGluVGV4dHVyZUNvb3Jkcy54LCBpblRleHR1cmVDb29yZHMueSk7Cgl2VGV4dHVyZUNvb3Jkc19TMCA9IHVub3JtVGV4Q29vcmRzICogdUF0bGFzU2l6ZUludl9TMDsKCXZUZXhJbmRleF9TMCA9IGZsb2F0KHRleElkeCk7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJc2tfUG9zaXRpb24gPSBpblBvc2l0aW9uLnh5MDE7Cn0KAAABAAAARQUAAGNvbnN0IGludCBrRmlsbEJXX1MxX2MwID0gMDsKY29uc3QgaW50IGtJbnZlcnNlRmlsbEJXX1MxX2MwID0gMjsKY29uc3QgaW50IGtJbnZlcnNlRmlsbEFBX1MxX2MwID0gMzsKdW5pZm9ybSBmbG9hdDQgdXJlY3RVbmlmb3JtX1MxX2MwOwp1bmlmb3JtIHNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMDsKaW4gZmxvYXQyIHZUZXh0dXJlQ29vcmRzX1MwOwpmbGF0IGluIGZsb2F0IHZUZXhJbmRleF9TMDsKaW4gaGFsZjQgdmluQ29sb3JfUzA7CmhhbGY0IFJlY3RfUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF8wX2luQ29sb3IgPSBfaW5wdXQ7CgloYWxmIGNvdmVyYWdlOwoJaWYgKGludCgxKSA9PSBrRmlsbEJXX1MxX2MwIHx8IGludCgxKSA9PSBrSW52ZXJzZUZpbGxCV19TMV9jMCkgCgl7CgkJY292ZXJhZ2UgPSBoYWxmKGFsbChncmVhdGVyVGhhbihmbG9hdDQoc2tfRnJhZ0Nvb3JkLnh5LCB1cmVjdFVuaWZvcm1fUzFfYzAuencpLCBmbG9hdDQodXJlY3RVbmlmb3JtX1MxX2MwLnh5LCBza19GcmFnQ29vcmQueHkpKSkpOwoJfQoJZWxzZSAKCXsKCQloYWxmNCBkaXN0czQgPSBzYXR1cmF0ZShoYWxmNCgxLjAsIDEuMCwgLTEuMCwgLTEuMCkgKiBoYWxmNChza19GcmFnQ29vcmQueHl4eSAtIHVyZWN0VW5pZm9ybV9TMV9jMCkpOwoJCWhhbGYyIGRpc3RzMiA9IChkaXN0czQueHkgKyBkaXN0czQuencpIC0gMS4wOwoJCWNvdmVyYWdlID0gZGlzdHMyLnggKiBkaXN0czIueTsKCX0KCWlmIChpbnQoMSkgPT0ga0ludmVyc2VGaWxsQldfUzFfYzAgfHwgaW50KDEpID09IGtJbnZlcnNlRmlsbEFBX1MxX2MwKSAKCXsKCQljb3ZlcmFnZSA9IDEuMCAtIGNvdmVyYWdlOwoJfQoJcmV0dXJuIGhhbGY0KGhhbGY0KGNvdmVyYWdlKSk7Cn0KaGFsZjQgQmxlbmRfUzEoaGFsZjQgX3NyYywgaGFsZjQgX2RzdCkgCnsKCXJldHVybiBibGVuZF9tb2R1bGF0ZShSZWN0X1MxX2MwKF9zcmMpLCBfc3JjKTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgQml0bWFwVGV4dAoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZpbkNvbG9yX1MwOwoJaGFsZjQgdGV4Q29sb3I7Cgl7CgkJdGV4Q29sb3IgPSBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzAsIHZUZXh0dXJlQ29vcmRzX1MwKS5ycnJyOwoJfQoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSB0ZXhDb2xvcjsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IEJsZW5kX1MxKG91dHB1dENvdmVyYWdlX1MwLCBoYWxmNCgxKSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzE7Cgl9Cn0KAAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADwAAAGluVGV4dHVyZUNvb3JkcwAAAAAA","AYQA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAAAAOQAAAAAAAQAAAABAMQAAAAA":"CgAAAExTS1PMAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0NCBpbkNpcmNsZUVkZ2U7Cm91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJdmluQ2lyY2xlRWRnZV9TMCA9IGluQ2lyY2xlRWRnZTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSB1bG9jYWxNYXRyaXhfUzAueHogKiBpblBvc2l0aW9uICsgdWxvY2FsTWF0cml4X1MwLnl3OwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgAAAADqAQAAaW4gZmxvYXQ0IHZpbkNpcmNsZUVkZ2VfUzA7CmluIGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgQ2lyY2xlR2VvbWV0cnlQcm9jZXNzb3IKCWZsb2F0NCBjaXJjbGVFZGdlOwoJY2lyY2xlRWRnZSA9IHZpbkNpcmNsZUVkZ2VfUzA7CgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmluQ29sb3JfUzA7CglmbG9hdCBkID0gbGVuZ3RoKGNpcmNsZUVkZ2UueHkpOwoJaGFsZiBkaXN0YW5jZVRvT3V0ZXJFZGdlID0gaGFsZihjaXJjbGVFZGdlLnogKiAoMS4wIC0gZCkpOwoJaGFsZiBlZGdlQWxwaGEgPSBzYXR1cmF0ZShkaXN0YW5jZVRvT3V0ZXJFZGdlKTsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoZWRnZUFscGhhKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADAAAAGluQ2lyY2xlRWRnZQAAAAA=","HUIAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAAAAAADUAANAAAAAAAAAIAAAABLAIABAAAAABAEGABBAMAAAAAAAAAAAAB2AAAAAAACAAAAAEBSAAAAA":"CgAAAExTS1M8AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMDsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzNfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxX2MwKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAC2AgAAdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxX2MwOwp1bmlmb3JtIHNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMTsKaW4gZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc18zX1MwOwpoYWxmNCBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzEsIHZUcmFuc2Zvcm1lZENvb3Jkc18zX1MwKTsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzBfYzAoX2lucHV0KTsKfQpoYWxmNCBCbGVuZF9TMShoYWxmNCBfc3JjLCBoYWxmNCBfZHN0KSAKewoJcmV0dXJuIGJsZW5kX21vZHVsYXRlKE1hdHJpeEVmZmVjdF9TMV9jMChfc3JjKSwgX3NyYyk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzAgPSBoYWxmNCgxKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBCbGVuZF9TMShvdXRwdXRDb2xvcl9TMCwgaGFsZjQoMSkpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAIAAAAcG9zaXRpb24KAAAAbG9jYWxDb29yZAAAAAAAAA==","HUIAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAAAAAALUAQBAAAQAAAAGQCBAMQACAAAAAAAACQAGAAAAAQAAAAAAAQQGAAAAA":"CgAAAExTS1M2AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAAAAHADAAB1bmlmb3JtIGZsb2F0NCB1Y2xhbXBfUzFfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKdW5pZm9ybSBzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7CmluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CglmbG9hdDIgaW5Db29yZCA9IHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwoJZmxvYXQyIHN1YnNldENvb3JkOwoJc3Vic2V0Q29vcmQueCA9IGluQ29vcmQueDsKCXN1YnNldENvb3JkLnkgPSBpbkNvb3JkLnk7CglmbG9hdDIgY2xhbXBlZENvb3JkOwoJY2xhbXBlZENvb3JkLnggPSBjbGFtcChzdWJzZXRDb29yZC54LCB1Y2xhbXBfUzFfYzAueCwgdWNsYW1wX1MxX2MwLnopOwoJY2xhbXBlZENvb3JkLnkgPSBzdWJzZXRDb29yZC55OwoJaGFsZjQgdGV4dHVyZUNvbG9yID0gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MxLCBjbGFtcGVkQ29vcmQpOwoJcmV0dXJuIHRleHR1cmVDb2xvcjsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzAoX2lucHV0KTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMCA9IGhhbGY0KDEpOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IE1hdHJpeEVmZmVjdF9TMShvdXRwdXRDb2xvcl9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0X1MxICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAIAAAAcG9zaXRpb24KAAAAbG9jYWxDb29yZAAAAAAAAA==","AYQQ5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAAAAOQAAAAAAAQAAAABAMQAAAAA":"CgAAAExTS1PMAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0NCBpbkNpcmNsZUVkZ2U7Cm91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJdmluQ2lyY2xlRWRnZV9TMCA9IGluQ2lyY2xlRWRnZTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSB1bG9jYWxNYXRyaXhfUzAueHogKiBpblBvc2l0aW9uICsgdWxvY2FsTWF0cml4X1MwLnl3OwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgAAAAB7AgAAaW4gZmxvYXQ0IHZpbkNpcmNsZUVkZ2VfUzA7CmluIGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgQ2lyY2xlR2VvbWV0cnlQcm9jZXNzb3IKCWZsb2F0NCBjaXJjbGVFZGdlOwoJY2lyY2xlRWRnZSA9IHZpbkNpcmNsZUVkZ2VfUzA7CgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmluQ29sb3JfUzA7CglmbG9hdCBkID0gbGVuZ3RoKGNpcmNsZUVkZ2UueHkpOwoJaGFsZiBkaXN0YW5jZVRvT3V0ZXJFZGdlID0gaGFsZihjaXJjbGVFZGdlLnogKiAoMS4wIC0gZCkpOwoJaGFsZiBlZGdlQWxwaGEgPSBzYXR1cmF0ZShkaXN0YW5jZVRvT3V0ZXJFZGdlKTsKCWhhbGYgZGlzdGFuY2VUb0lubmVyRWRnZSA9IGhhbGYoY2lyY2xlRWRnZS56ICogKGQgLSBjaXJjbGVFZGdlLncpKTsKCWhhbGYgaW5uZXJBbHBoYSA9IHNhdHVyYXRlKGRpc3RhbmNlVG9Jbm5lckVkZ2UpOwoJZWRnZUFscGhhICo9IGlubmVyQWxwaGE7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGVkZ2VBbHBoYSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADAAAAGluQ2lyY2xlRWRnZQAAAAA=","HWQACAAAABAAADAAAIOAAAAADIIAAIRODAAP577774DSAIAA737777YBAAAAAAAAAAAKAAYAAAACAAAAAAACCAYAAA":"CgAAAExTS1ONAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQgY292ZXJhZ2U7CmluIGhhbGY0IGNvbG9yOwppbiBmbG9hdDQgZ2VvbVN1YnNldDsKZmxhdCBvdXQgaGFsZjQgdmNvbG9yX1MwOwpvdXQgZmxvYXQgdmNvdmVyYWdlX1MwOwpmbGF0IG91dCBmbG9hdDQgdmdlb21TdWJzZXRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQyIHBvc2l0aW9uID0gcG9zaXRpb24ueHk7Cgl2Y29sb3JfUzAgPSBjb2xvcjsKCXZjb3ZlcmFnZV9TMCA9IGNvdmVyYWdlOwoJdmdlb21TdWJzZXRfUzAgPSBnZW9tU3Vic2V0OwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAAEAAAB5AgAAZmxhdCBpbiBoYWxmNCB2Y29sb3JfUzA7CmluIGZsb2F0IHZjb3ZlcmFnZV9TMDsKZmxhdCBpbiBmbG9hdDQgdmdlb21TdWJzZXRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7CglmbG9hdCBjb3ZlcmFnZSA9IHZjb3ZlcmFnZV9TMDsKCWZsb2F0NCBnZW9TdWJzZXQ7CglnZW9TdWJzZXQgPSB2Z2VvbVN1YnNldF9TMDsKCWhhbGY0IGRpc3RzNCA9IGNsYW1wKGhhbGY0KDEsIDEsIC0xLCAtMSkgKiBoYWxmNChza19GcmFnQ29vcmQueHl4eSAtIGdlb1N1YnNldCksIDAsIDEpOwoJaGFsZjIgZGlzdHMyID0gZGlzdHM0Lnh5ICsgZGlzdHM0Lnp3IC0gMTsKCWhhbGYgc3Vic2V0Q292ZXJhZ2UgPSBkaXN0czIueCAqIGRpc3RzMi55OwoJY292ZXJhZ2UgPSBtaW4oY292ZXJhZ2UsIHN1YnNldENvdmVyYWdlKTsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoaGFsZihjb3ZlcmFnZSkpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAIAAAAcG9zaXRpb24IAAAAY292ZXJhZ2UFAAAAY29sb3IAAAAKAAAAZ2VvbVN1YnNldAAAAAAAAA==","AYQA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAACABZQA6AAAEAAAAAAAIADQAAAAIAAAAAAAIIDA":"CgAAAExTS1PMAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0NCBpbkNpcmNsZUVkZ2U7Cm91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJdmluQ2lyY2xlRWRnZV9TMCA9IGluQ2lyY2xlRWRnZTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSB1bG9jYWxNYXRyaXhfUzAueHogKiBpblBvc2l0aW9uICsgdWxvY2FsTWF0cml4X1MwLnl3OwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgEAAACPAwAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CmluIGZsb2F0NCB2aW5DaXJjbGVFZGdlX1MwOwppbiBoYWxmNCB2aW5Db2xvcl9TMDsKaGFsZjQgQ2lyY3VsYXJSUmVjdF9TMShoYWxmNCBfaW5wdXQpIAp7CglmbG9hdDIgZHh5MCA9IHVpbm5lclJlY3RfUzEuTFQgLSBza19GcmFnQ29vcmQueHk7CglmbG9hdDIgZHh5MSA9IHNrX0ZyYWdDb29yZC54eSAtIHVpbm5lclJlY3RfUzEuUkI7CglmbG9hdDIgZHh5ID0gbWF4KG1heChkeHkwLCBkeHkxKSwgMC4wKTsKCWhhbGYgYWxwaGEgPSBoYWxmKHNhdHVyYXRlKHVyYWRpdXNQbHVzSGFsZl9TMS54IC0gbGVuZ3RoKGR4eSkpKTsKCWFscGhhID0gMS4wIC0gYWxwaGE7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIENpcmNsZUdlb21ldHJ5UHJvY2Vzc29yCglmbG9hdDQgY2lyY2xlRWRnZTsKCWNpcmNsZUVkZ2UgPSB2aW5DaXJjbGVFZGdlX1MwOwoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZpbkNvbG9yX1MwOwoJZmxvYXQgZCA9IGxlbmd0aChjaXJjbGVFZGdlLnh5KTsKCWhhbGYgZGlzdGFuY2VUb091dGVyRWRnZSA9IGhhbGYoY2lyY2xlRWRnZS56ICogKDEuMCAtIGQpKTsKCWhhbGYgZWRnZUFscGhhID0gc2F0dXJhdGUoZGlzdGFuY2VUb091dGVyRWRnZSk7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGVkZ2VBbHBoYSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBDaXJjdWxhclJSZWN0X1MxKG91dHB1dENvdmVyYWdlX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADAAAAGluQ2lyY2xlRWRnZQAAAAA=","HUIAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAAAAAADUAANAAAAAAIBAIAAAABLCIIBAAAAABAEGABBAMAACAIAAAAAAAB2AAAAAAACAAAAAEBSAAAAA":"CgAAAExTS1M8AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMDsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzNfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxX2MwKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAADJAwAAdW5pZm9ybSBmbG9hdDQgdWNsYW1wX1MxX2MwX2MwOwp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzA7CnVuaWZvcm0gc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MxOwppbiBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzNfUzA7CmhhbGY0IFRleHR1cmVFZmZlY3RfUzFfYzBfYzAoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGluQ29vcmQgPSB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKCWZsb2F0MiBzdWJzZXRDb29yZDsKCXN1YnNldENvb3JkLnggPSBpbkNvb3JkLng7CglzdWJzZXRDb29yZC55ID0gaW5Db29yZC55OwoJZmxvYXQyIGNsYW1wZWRDb29yZDsKCWNsYW1wZWRDb29yZCA9IGNsYW1wKHN1YnNldENvb3JkLCB1Y2xhbXBfUzFfYzBfYzAueHksIHVjbGFtcF9TMV9jMF9jMC56dyk7CgloYWxmNCB0ZXh0dXJlQ29sb3IgPSBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzEsIGNsYW1wZWRDb29yZCk7CglyZXR1cm4gdGV4dHVyZUNvbG9yOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gVGV4dHVyZUVmZmVjdF9TMV9jMF9jMChfaW5wdXQpOwp9CmhhbGY0IEJsZW5kX1MxKGhhbGY0IF9zcmMsIGhhbGY0IF9kc3QpIAp7CglyZXR1cm4gYmxlbmRfbW9kdWxhdGUoTWF0cml4RWZmZWN0X1MxX2MwKF9zcmMpLCBfc3JjKTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMCA9IGhhbGY0KDEpOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IEJsZW5kX1MxKG91dHB1dENvbG9yX1MwLCBoYWxmNCgxKSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0X1MxICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAIAAAAcG9zaXRpb24KAAAAbG9jYWxDb29yZAAAAAAAAA==","FAAQMYAAMAAAEADAAABAEYAAAICIAB5AABQAAAQAMAAAEATAAABAIIGAAEDCBYQCA4AAAAAAEADZABYAAAIAAAAAACQAGAAAAAQAAAAAAAQQG":"CgAAAExTS1PUCwAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0NCByYWRpaV9zZWxlY3RvcjsKaW4gZmxvYXQ0IGNvcm5lcl9hbmRfcmFkaXVzX291dHNldHM7CmluIGZsb2F0NCBhYV9ibG9hdF9hbmRfY292ZXJhZ2U7CmluIGZsb2F0NCByYWRpaV94OwppbiBmbG9hdDQgcmFkaWlfeTsKaW4gZmxvYXQ0IHNrZXc7CmluIGZsb2F0MiB0cmFuc2xhdGVfYW5kX2xvY2Fscm90YXRlOwppbiBoYWxmNCBjb2xvcjsKZmxhdCBvdXQgaGFsZjQgdmNvbG9yX1MwOwpvdXQgZmxvYXQyIHZhcmNjb29yZF9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgRmlsbFJSZWN0T3A6OlByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7CglmbG9hdCBhYV9ibG9hdF9tdWx0aXBsaWVyID0gMTsKCWZsb2F0MiBjb3JuZXIgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnh5OwoJZmxvYXQyIHJhZGl1c19vdXRzZXQgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnp3OwoJZmxvYXQyIGFhX2Jsb2F0X2RpcmVjdGlvbiA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS54eTsKCWZsb2F0IGlzX2xpbmVhcl9jb3ZlcmFnZSA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS53OwoJZmxvYXQyIHBpeGVsbGVuZ3RoID0gaW52ZXJzZXNxcnQoZmxvYXQyKGRvdChza2V3Lnh6LCBza2V3Lnh6KSwgZG90KHNrZXcueXcsIHNrZXcueXcpKSk7CglmbG9hdDQgbm9ybWFsaXplZF9heGlzX2RpcnMgPSBza2V3ICogcGl4ZWxsZW5ndGgueHl4eTsKCWZsb2F0MiBheGlzd2lkdGhzID0gKGFicyhub3JtYWxpemVkX2F4aXNfZGlycy54eSkgKyBhYnMobm9ybWFsaXplZF9heGlzX2RpcnMuencpKTsKCWZsb2F0MiBhYV9ibG9hdHJhZGl1cyA9IGF4aXN3aWR0aHMgKiBwaXhlbGxlbmd0aCAqIC41OwoJZmxvYXQ0IHJhZGlpX2FuZF9uZWlnaGJvcnMgPSByYWRpaV9zZWxlY3RvciogZmxvYXQ0eDQocmFkaWlfeCwgcmFkaWlfeSwgcmFkaWlfeC55eHd6LCByYWRpaV95Lnd6eXgpOwoJZmxvYXQyIHJhZGlpID0gcmFkaWlfYW5kX25laWdoYm9ycy54eTsKCWZsb2F0MiBuZWlnaGJvcl9yYWRpaSA9IHJhZGlpX2FuZF9uZWlnaGJvcnMuenc7CglmbG9hdCBjb3ZlcmFnZV9tdWx0aXBsaWVyID0gMTsKCWlmIChhbnkoZ3JlYXRlclRoYW4oYWFfYmxvYXRyYWRpdXMsIGZsb2F0MigxKSkpKSAKCXsKCQljb3JuZXIgPSBtYXgoYWJzKGNvcm5lciksIGFhX2Jsb2F0cmFkaXVzKSAqIHNpZ24oY29ybmVyKTsKCQljb3ZlcmFnZV9tdWx0aXBsaWVyID0gMSAvIChtYXgoYWFfYmxvYXRyYWRpdXMueCwgMSkgKiBtYXgoYWFfYmxvYXRyYWRpdXMueSwgMSkpOwoJCXJhZGlpID0gZmxvYXQyKDApOwoJfQoJZmxvYXQgY292ZXJhZ2UgPSBhYV9ibG9hdF9hbmRfY292ZXJhZ2UuejsKCWlmIChhbnkobGVzc1RoYW4ocmFkaWksIGFhX2Jsb2F0cmFkaXVzICogMS41KSkpIAoJewoJCXJhZGlpID0gZmxvYXQyKDApOwoJCWFhX2Jsb2F0X2RpcmVjdGlvbiA9IHNpZ24oY29ybmVyKTsKCQlpZiAoY292ZXJhZ2UgPiAuNSkgCgkJewoJCQlhYV9ibG9hdF9kaXJlY3Rpb24gPSAtYWFfYmxvYXRfZGlyZWN0aW9uOwoJCX0KCQlpc19saW5lYXJfY292ZXJhZ2UgPSAxOwoJfQoJZWxzZSAKCXsKCQlyYWRpaSA9IGNsYW1wKHJhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQluZWlnaGJvcl9yYWRpaSA9IGNsYW1wKG5laWdoYm9yX3JhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQlmbG9hdDIgc3BhY2luZyA9IDIgLSByYWRpaSAtIG5laWdoYm9yX3JhZGlpOwoJCWZsb2F0MiBleHRyYV9wYWQgPSBtYXgocGl4ZWxsZW5ndGggKiAuMDYyNSAtIHNwYWNpbmcsIGZsb2F0MigwKSk7CgkJcmFkaWkgLT0gZXh0cmFfcGFkICogLjU7Cgl9CglmbG9hdDIgYWFfb3V0c2V0ID0gYWFfYmxvYXRfZGlyZWN0aW9uICogYWFfYmxvYXRyYWRpdXMgKiBhYV9ibG9hdF9tdWx0aXBsaWVyOwoJZmxvYXQyIHZlcnRleHBvcyA9IGNvcm5lciArIHJhZGl1c19vdXRzZXQgKiByYWRpaSArIGFhX291dHNldDsKCWlmIChjb3ZlcmFnZSA+IC41KSAKCXsKCQlpZiAoYWFfYmxvYXRfZGlyZWN0aW9uLnggIT0gMCAmJiB2ZXJ0ZXhwb3MueCAqIGNvcm5lci54IDwgMCkgCgkJewoJCQlmbG9hdCBiYWNrc2V0ID0gYWJzKHZlcnRleHBvcy54KTsKCQkJdmVydGV4cG9zLnggPSAwOwoJCQl2ZXJ0ZXhwb3MueSArPSBiYWNrc2V0ICogc2lnbihjb3JuZXIueSkgKiBwaXhlbGxlbmd0aC55L3BpeGVsbGVuZ3RoLng7CgkJCWNvdmVyYWdlID0gKGNvdmVyYWdlIC0gLjUpICogYWJzKGNvcm5lci54KSAvIChhYnMoY29ybmVyLngpICsgYmFja3NldCkgKyAuNTsKCQl9CgkJaWYgKGFhX2Jsb2F0X2RpcmVjdGlvbi55ICE9IDAgJiYgdmVydGV4cG9zLnkgKiBjb3JuZXIueSA8IDApIAoJCXsKCQkJZmxvYXQgYmFja3NldCA9IGFicyh2ZXJ0ZXhwb3MueSk7CgkJCXZlcnRleHBvcy55ID0gMDsKCQkJdmVydGV4cG9zLnggKz0gYmFja3NldCAqIHNpZ24oY29ybmVyLngpICogcGl4ZWxsZW5ndGgueC9waXhlbGxlbmd0aC55OwoJCQljb3ZlcmFnZSA9IChjb3ZlcmFnZSAtIC41KSAqIGFicyhjb3JuZXIueSkgLyAoYWJzKGNvcm5lci55KSArIGJhY2tzZXQpICsgLjU7CgkJfQoJfQoJZmxvYXQyeDIgc2tld21hdHJpeCA9IGZsb2F0MngyKHNrZXcueHksIHNrZXcuencpOwoJZmxvYXQyIGRldmNvb3JkID0gdmVydGV4cG9zICogc2tld21hdHJpeCArIHRyYW5zbGF0ZV9hbmRfbG9jYWxyb3RhdGUueHk7CglpZiAoMCAhPSBpc19saW5lYXJfY292ZXJhZ2UpIAoJewoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MigwLCBjb3ZlcmFnZSAqIGNvdmVyYWdlX211bHRpcGxpZXIpOwoJfQoJZWxzZSAKCXsKCQlmbG9hdDIgYXJjY29vcmQgPSAxIC0gYWJzKHJhZGl1c19vdXRzZXQpICsgYWFfb3V0c2V0L3JhZGlpICogY29ybmVyOwoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MihhcmNjb29yZC54KzEsIGFyY2Nvb3JkLnkpOwoJfQoJc2tfUG9zaXRpb24gPSBkZXZjb29yZC54eTAxOwp9CgEAAADUAwAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CmZsYXQgaW4gaGFsZjQgdmNvbG9yX1MwOwppbiBmbG9hdDIgdmFyY2Nvb3JkX1MwOwpoYWxmNCBDaXJjdWxhclJSZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCWZsb2F0MiBkeHkwID0gdWlubmVyUmVjdF9TMS5MVCAtIHNrX0ZyYWdDb29yZC54eTsKCWZsb2F0MiBkeHkxID0gc2tfRnJhZ0Nvb3JkLnh5IC0gdWlubmVyUmVjdF9TMS5SQjsKCWZsb2F0MiBkeHkgPSBtYXgobWF4KGR4eTAsIGR4eTEpLCAwLjApOwoJaGFsZiBhbHBoYSA9IGhhbGYoc2F0dXJhdGUodXJhZGl1c1BsdXNIYWxmX1MxLnggLSBsZW5ndGgoZHh5KSkpOwoJcmV0dXJuIF9pbnB1dCAqIGFscGhhOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBGaWxsUlJlY3RPcDo6UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJZmxvYXQgeF9wbHVzXzE9dmFyY2Nvb3JkX1MwLngsIHk9dmFyY2Nvb3JkX1MwLnk7CgloYWxmIGNvdmVyYWdlOwoJaWYgKDAgPT0geF9wbHVzXzEpIAoJewoJCWNvdmVyYWdlID0gaGFsZih5KTsKCX0KCWVsc2UgCgl7CgkJZmxvYXQgZm4gPSB4X3BsdXNfMSAqICh4X3BsdXNfMSAtIDIpOwoJCWZuID0gZm1hKHkseSwgZm4pOwoJCWZsb2F0IGZud2lkdGggPSBmd2lkdGgoZm4pOwoJCWNvdmVyYWdlID0gLjUgLSBoYWxmKGZuL2Zud2lkdGgpOwoJCWNvdmVyYWdlID0gY2xhbXAoY292ZXJhZ2UsIDAsIDEpOwoJfQoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChjb3ZlcmFnZSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBDaXJjdWxhclJSZWN0X1MxKG91dHB1dENvdmVyYWdlX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoBAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAACAAAAA4AAAByYWRpaV9zZWxlY3RvcgAAGQAAAGNvcm5lcl9hbmRfcmFkaXVzX291dHNldHMAAAAVAAAAYWFfYmxvYXRfYW5kX2NvdmVyYWdlAAAABwAAAHJhZGlpX3gABwAAAHJhZGlpX3kABAAAAHNrZXcZAAAAdHJhbnNsYXRlX2FuZF9sb2NhbHJvdGF0ZQAAAAUAAABjb2xvcgAAAAAAAAA=","AYQA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAACAAYQADAAAEAFEURUKQKAAAYAAAAAAAAIAAAABSCICWKY2FAEAAAMAAAAAAAAAAAAAIADQAAAAIAAAAAAAIIDAAAA":"CgAAAExTS1PMAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0NCBpbkNpcmNsZUVkZ2U7Cm91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJdmluQ2lyY2xlRWRnZV9TMCA9IGluQ2lyY2xlRWRnZTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSB1bG9jYWxNYXRyaXhfUzAueHogKiBpblBvc2l0aW9uICsgdWxvY2FsTWF0cml4X1MwLnl3OwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgEAAAB+BQAAY29uc3QgaW50IGtGaWxsQldfUzFfYzAgPSAwOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQldfUzFfYzAgPSAyOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQUFfUzFfYzAgPSAzOwp1bmlmb3JtIGZsb2F0NCB1cmVjdFVuaWZvcm1fUzFfYzA7CmluIGZsb2F0NCB2aW5DaXJjbGVFZGdlX1MwOwppbiBoYWxmNCB2aW5Db2xvcl9TMDsKaGFsZjQgUmVjdF9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzBfaW5Db2xvciA9IF9pbnB1dDsKCWhhbGYgY292ZXJhZ2U7CglpZiAoaW50KDEpID09IGtGaWxsQldfUzFfYzAgfHwgaW50KDEpID09IGtJbnZlcnNlRmlsbEJXX1MxX2MwKSAKCXsKCQljb3ZlcmFnZSA9IGhhbGYoYWxsKGdyZWF0ZXJUaGFuKGZsb2F0NChza19GcmFnQ29vcmQueHksIHVyZWN0VW5pZm9ybV9TMV9jMC56dyksIGZsb2F0NCh1cmVjdFVuaWZvcm1fUzFfYzAueHksIHNrX0ZyYWdDb29yZC54eSkpKSk7Cgl9CgllbHNlIAoJewoJCWhhbGY0IGRpc3RzNCA9IHNhdHVyYXRlKGhhbGY0KDEuMCwgMS4wLCAtMS4wLCAtMS4wKSAqIGhhbGY0KHNrX0ZyYWdDb29yZC54eXh5IC0gdXJlY3RVbmlmb3JtX1MxX2MwKSk7CgkJaGFsZjIgZGlzdHMyID0gKGRpc3RzNC54eSArIGRpc3RzNC56dykgLSAxLjA7CgkJY292ZXJhZ2UgPSBkaXN0czIueCAqIGRpc3RzMi55OwoJfQoJaWYgKGludCgxKSA9PSBrSW52ZXJzZUZpbGxCV19TMV9jMCB8fCBpbnQoMSkgPT0ga0ludmVyc2VGaWxsQUFfUzFfYzApIAoJewoJCWNvdmVyYWdlID0gMS4wIC0gY292ZXJhZ2U7Cgl9CglyZXR1cm4gaGFsZjQoaGFsZjQoY292ZXJhZ2UpKTsKfQpoYWxmNCBCbGVuZF9TMShoYWxmNCBfc3JjLCBoYWxmNCBfZHN0KSAKewoJcmV0dXJuIGJsZW5kX21vZHVsYXRlKFJlY3RfUzFfYzAoX3NyYyksIF9zcmMpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQ0IGNpcmNsZUVkZ2U7CgljaXJjbGVFZGdlID0gdmluQ2lyY2xlRWRnZV9TMDsKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWZsb2F0IGQgPSBsZW5ndGgoY2lyY2xlRWRnZS54eSk7CgloYWxmIGRpc3RhbmNlVG9PdXRlckVkZ2UgPSBoYWxmKGNpcmNsZUVkZ2UueiAqICgxLjAgLSBkKSk7CgloYWxmIGVkZ2VBbHBoYSA9IHNhdHVyYXRlKGRpc3RhbmNlVG9PdXRlckVkZ2UpOwoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChlZGdlQWxwaGEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gQmxlbmRfUzEob3V0cHV0Q292ZXJhZ2VfUzAsIGhhbGY0KDEpKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACgAAAGluUG9zaXRpb24AAAcAAABpbkNvbG9yAAwAAABpbkNpcmNsZUVkZ2UAAAAA","HUIAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAAAAAALUAQBAEAQAAAAGQCBAMQACAIAAAAAACQAGAAAAAQAAAAAAAQQGAAAAA":"CgAAAExTS1M2AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAAAAE0DAAB1bmlmb3JtIGZsb2F0NCB1Y2xhbXBfUzFfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKdW5pZm9ybSBzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7CmluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CglmbG9hdDIgaW5Db29yZCA9IHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwoJZmxvYXQyIHN1YnNldENvb3JkOwoJc3Vic2V0Q29vcmQueCA9IGluQ29vcmQueDsKCXN1YnNldENvb3JkLnkgPSBpbkNvb3JkLnk7CglmbG9hdDIgY2xhbXBlZENvb3JkOwoJY2xhbXBlZENvb3JkID0gY2xhbXAoc3Vic2V0Q29vcmQsIHVjbGFtcF9TMV9jMC54eSwgdWNsYW1wX1MxX2MwLnp3KTsKCWhhbGY0IHRleHR1cmVDb2xvciA9IHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMSwgY2xhbXBlZENvb3JkKTsKCXJldHVybiB0ZXh0dXJlQ29sb3I7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBUZXh0dXJlRWZmZWN0X1MxX2MwKF9pbnB1dCk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzAgPSBoYWxmNCgxKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBNYXRyaXhFZmZlY3RfUzEob3V0cHV0Q29sb3JfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACAAAAHBvc2l0aW9uCgAAAGxvY2FsQ29vcmQAAAAAAAA=","FAAQMYAAMAAAEADAAABAEYAAAICIAB5AABQAAAQAMAAAEATAAABAIIGAAEDCBYQCA4AAAAAAEAKPABAAAAAAB2AAAAAAACAAAAAEBSAAAAAAA":"CgAAAExTS1PUCwAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0NCByYWRpaV9zZWxlY3RvcjsKaW4gZmxvYXQ0IGNvcm5lcl9hbmRfcmFkaXVzX291dHNldHM7CmluIGZsb2F0NCBhYV9ibG9hdF9hbmRfY292ZXJhZ2U7CmluIGZsb2F0NCByYWRpaV94OwppbiBmbG9hdDQgcmFkaWlfeTsKaW4gZmxvYXQ0IHNrZXc7CmluIGZsb2F0MiB0cmFuc2xhdGVfYW5kX2xvY2Fscm90YXRlOwppbiBoYWxmNCBjb2xvcjsKZmxhdCBvdXQgaGFsZjQgdmNvbG9yX1MwOwpvdXQgZmxvYXQyIHZhcmNjb29yZF9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgRmlsbFJSZWN0T3A6OlByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7CglmbG9hdCBhYV9ibG9hdF9tdWx0aXBsaWVyID0gMTsKCWZsb2F0MiBjb3JuZXIgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnh5OwoJZmxvYXQyIHJhZGl1c19vdXRzZXQgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnp3OwoJZmxvYXQyIGFhX2Jsb2F0X2RpcmVjdGlvbiA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS54eTsKCWZsb2F0IGlzX2xpbmVhcl9jb3ZlcmFnZSA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS53OwoJZmxvYXQyIHBpeGVsbGVuZ3RoID0gaW52ZXJzZXNxcnQoZmxvYXQyKGRvdChza2V3Lnh6LCBza2V3Lnh6KSwgZG90KHNrZXcueXcsIHNrZXcueXcpKSk7CglmbG9hdDQgbm9ybWFsaXplZF9heGlzX2RpcnMgPSBza2V3ICogcGl4ZWxsZW5ndGgueHl4eTsKCWZsb2F0MiBheGlzd2lkdGhzID0gKGFicyhub3JtYWxpemVkX2F4aXNfZGlycy54eSkgKyBhYnMobm9ybWFsaXplZF9heGlzX2RpcnMuencpKTsKCWZsb2F0MiBhYV9ibG9hdHJhZGl1cyA9IGF4aXN3aWR0aHMgKiBwaXhlbGxlbmd0aCAqIC41OwoJZmxvYXQ0IHJhZGlpX2FuZF9uZWlnaGJvcnMgPSByYWRpaV9zZWxlY3RvciogZmxvYXQ0eDQocmFkaWlfeCwgcmFkaWlfeSwgcmFkaWlfeC55eHd6LCByYWRpaV95Lnd6eXgpOwoJZmxvYXQyIHJhZGlpID0gcmFkaWlfYW5kX25laWdoYm9ycy54eTsKCWZsb2F0MiBuZWlnaGJvcl9yYWRpaSA9IHJhZGlpX2FuZF9uZWlnaGJvcnMuenc7CglmbG9hdCBjb3ZlcmFnZV9tdWx0aXBsaWVyID0gMTsKCWlmIChhbnkoZ3JlYXRlclRoYW4oYWFfYmxvYXRyYWRpdXMsIGZsb2F0MigxKSkpKSAKCXsKCQljb3JuZXIgPSBtYXgoYWJzKGNvcm5lciksIGFhX2Jsb2F0cmFkaXVzKSAqIHNpZ24oY29ybmVyKTsKCQljb3ZlcmFnZV9tdWx0aXBsaWVyID0gMSAvIChtYXgoYWFfYmxvYXRyYWRpdXMueCwgMSkgKiBtYXgoYWFfYmxvYXRyYWRpdXMueSwgMSkpOwoJCXJhZGlpID0gZmxvYXQyKDApOwoJfQoJZmxvYXQgY292ZXJhZ2UgPSBhYV9ibG9hdF9hbmRfY292ZXJhZ2UuejsKCWlmIChhbnkobGVzc1RoYW4ocmFkaWksIGFhX2Jsb2F0cmFkaXVzICogMS41KSkpIAoJewoJCXJhZGlpID0gZmxvYXQyKDApOwoJCWFhX2Jsb2F0X2RpcmVjdGlvbiA9IHNpZ24oY29ybmVyKTsKCQlpZiAoY292ZXJhZ2UgPiAuNSkgCgkJewoJCQlhYV9ibG9hdF9kaXJlY3Rpb24gPSAtYWFfYmxvYXRfZGlyZWN0aW9uOwoJCX0KCQlpc19saW5lYXJfY292ZXJhZ2UgPSAxOwoJfQoJZWxzZSAKCXsKCQlyYWRpaSA9IGNsYW1wKHJhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQluZWlnaGJvcl9yYWRpaSA9IGNsYW1wKG5laWdoYm9yX3JhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQlmbG9hdDIgc3BhY2luZyA9IDIgLSByYWRpaSAtIG5laWdoYm9yX3JhZGlpOwoJCWZsb2F0MiBleHRyYV9wYWQgPSBtYXgocGl4ZWxsZW5ndGggKiAuMDYyNSAtIHNwYWNpbmcsIGZsb2F0MigwKSk7CgkJcmFkaWkgLT0gZXh0cmFfcGFkICogLjU7Cgl9CglmbG9hdDIgYWFfb3V0c2V0ID0gYWFfYmxvYXRfZGlyZWN0aW9uICogYWFfYmxvYXRyYWRpdXMgKiBhYV9ibG9hdF9tdWx0aXBsaWVyOwoJZmxvYXQyIHZlcnRleHBvcyA9IGNvcm5lciArIHJhZGl1c19vdXRzZXQgKiByYWRpaSArIGFhX291dHNldDsKCWlmIChjb3ZlcmFnZSA+IC41KSAKCXsKCQlpZiAoYWFfYmxvYXRfZGlyZWN0aW9uLnggIT0gMCAmJiB2ZXJ0ZXhwb3MueCAqIGNvcm5lci54IDwgMCkgCgkJewoJCQlmbG9hdCBiYWNrc2V0ID0gYWJzKHZlcnRleHBvcy54KTsKCQkJdmVydGV4cG9zLnggPSAwOwoJCQl2ZXJ0ZXhwb3MueSArPSBiYWNrc2V0ICogc2lnbihjb3JuZXIueSkgKiBwaXhlbGxlbmd0aC55L3BpeGVsbGVuZ3RoLng7CgkJCWNvdmVyYWdlID0gKGNvdmVyYWdlIC0gLjUpICogYWJzKGNvcm5lci54KSAvIChhYnMoY29ybmVyLngpICsgYmFja3NldCkgKyAuNTsKCQl9CgkJaWYgKGFhX2Jsb2F0X2RpcmVjdGlvbi55ICE9IDAgJiYgdmVydGV4cG9zLnkgKiBjb3JuZXIueSA8IDApIAoJCXsKCQkJZmxvYXQgYmFja3NldCA9IGFicyh2ZXJ0ZXhwb3MueSk7CgkJCXZlcnRleHBvcy55ID0gMDsKCQkJdmVydGV4cG9zLnggKz0gYmFja3NldCAqIHNpZ24oY29ybmVyLngpICogcGl4ZWxsZW5ndGgueC9waXhlbGxlbmd0aC55OwoJCQljb3ZlcmFnZSA9IChjb3ZlcmFnZSAtIC41KSAqIGFicyhjb3JuZXIueSkgLyAoYWJzKGNvcm5lci55KSArIGJhY2tzZXQpICsgLjU7CgkJfQoJfQoJZmxvYXQyeDIgc2tld21hdHJpeCA9IGZsb2F0MngyKHNrZXcueHksIHNrZXcuencpOwoJZmxvYXQyIGRldmNvb3JkID0gdmVydGV4cG9zICogc2tld21hdHJpeCArIHRyYW5zbGF0ZV9hbmRfbG9jYWxyb3RhdGUueHk7CglpZiAoMCAhPSBpc19saW5lYXJfY292ZXJhZ2UpIAoJewoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MigwLCBjb3ZlcmFnZSAqIGNvdmVyYWdlX211bHRpcGxpZXIpOwoJfQoJZWxzZSAKCXsKCQlmbG9hdDIgYXJjY29vcmQgPSAxIC0gYWJzKHJhZGl1c19vdXRzZXQpICsgYWFfb3V0c2V0L3JhZGlpICogY29ybmVyOwoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MihhcmNjb29yZC54KzEsIGFyY2Nvb3JkLnkpOwoJfQoJc2tfUG9zaXRpb24gPSBkZXZjb29yZC54eTAxOwp9CgEAAACbBAAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBmbG9hdDIgdWludlJhZGlpWFlfUzE7CmZsYXQgaW4gaGFsZjQgdmNvbG9yX1MwOwppbiBmbG9hdDIgdmFyY2Nvb3JkX1MwOwpoYWxmNCBFbGxpcHRpY2FsUlJlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MxLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MxLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CglmbG9hdDIgWiA9IGR4eSAqIHVpbnZSYWRpaVhZX1MxLnh5OwoJaGFsZiBpbXBsaWNpdCA9IGhhbGYoZG90KFosIGR4eSkgLSAxLjApOwoJaGFsZiBncmFkX2RvdCA9IGhhbGYoNC4wICogZG90KFosIFopKTsKCWdyYWRfZG90ID0gbWF4KGdyYWRfZG90LCAxLjBlLTQpOwoJaGFsZiBhcHByb3hfZGlzdCA9IGltcGxpY2l0ICogaGFsZihpbnZlcnNlc3FydChncmFkX2RvdCkpOwoJaGFsZiBhbHBoYSA9IGNsYW1wKDAuNSArIGFwcHJveF9kaXN0LCAwLjAsIDEuMCk7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIEZpbGxSUmVjdE9wOjpQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7CglmbG9hdCB4X3BsdXNfMT12YXJjY29vcmRfUzAueCwgeT12YXJjY29vcmRfUzAueTsKCWhhbGYgY292ZXJhZ2U7CglpZiAoMCA9PSB4X3BsdXNfMSkgCgl7CgkJY292ZXJhZ2UgPSBoYWxmKHkpOwoJfQoJZWxzZSAKCXsKCQlmbG9hdCBmbiA9IHhfcGx1c18xICogKHhfcGx1c18xIC0gMik7CgkJZm4gPSBmbWEoeSx5LCBmbik7CgkJZmxvYXQgZm53aWR0aCA9IGZ3aWR0aChmbik7CgkJY292ZXJhZ2UgPSAuNSAtIGhhbGYoZm4vZm53aWR0aCk7CgkJY292ZXJhZ2UgPSBjbGFtcChjb3ZlcmFnZSwgMCwgMSk7Cgl9CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGNvdmVyYWdlKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IEVsbGlwdGljYWxSUmVjdF9TMShvdXRwdXRDb3ZlcmFnZV9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzE7Cgl9Cn0KAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAIAAAADgAAAHJhZGlpX3NlbGVjdG9yAAAZAAAAY29ybmVyX2FuZF9yYWRpdXNfb3V0c2V0cwAAABUAAABhYV9ibG9hdF9hbmRfY292ZXJhZ2UAAAAHAAAAcmFkaWlfeAAHAAAAcmFkaWlfeQAEAAAAc2tldxkAAAB0cmFuc2xhdGVfYW5kX2xvY2Fscm90YXRlAAAABQAAAGNvbG9yAAAAAAAAAA==","HVIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAGIBIAAABAAAAANAEAAAAAAAAAAAAAABAAOAAAABAAAAAAABBAMAAAAA":"CgAAAExTS1N0AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBoYWxmNCBjb2xvcjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7CmZsYXQgb3V0IGhhbGY0IHZjb2xvcl9TMDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfMl9TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzEpICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAAAAHMCAAB1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CnVuaWZvcm0gc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MxOwpmbGF0IGluIGhhbGY0IHZjb2xvcl9TMDsKaW4gZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwpoYWxmNCBUZXh0dXJlRWZmZWN0X1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzEsIHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwKS5ycnJyOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMShoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gVGV4dHVyZUVmZmVjdF9TMV9jMChfaW5wdXQpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gTWF0cml4RWZmZWN0X1MxKG91dHB1dENvdmVyYWdlX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAIAAAAcG9zaXRpb24FAAAAY29sb3IAAAAKAAAAbG9jYWxDb29yZAAAAAAAAA=="}} \ No newline at end of file +{"platform":"android","name":"SM G970N","engineRevision":"d44b5a94c976fbb65815374f61ab5392a220b084","data":{"DAQAAAAAAABGAABAYAAQAIHCAIAYAQUBAEAAAAAAEAAAAAAAAAAAAIAHSADQAAAQAAAAAAFAAMAAAABAAAAAAABBAM":"CgAAAExTS1MWAgAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQyIHVBdGxhc1NpemVJbnZfUzA7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiB1c2hvcnQyIGluVGV4dHVyZUNvb3JkczsKb3V0IGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKZmxhdCBvdXQgZmxvYXQgdlRleEluZGV4X1MwOwpvdXQgaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIEJpdG1hcFRleHQKCWludCB0ZXhJZHggPSAwOwoJZmxvYXQyIHVub3JtVGV4Q29vcmRzID0gZmxvYXQyKGluVGV4dHVyZUNvb3Jkcy54LCBpblRleHR1cmVDb29yZHMueSk7Cgl2VGV4dHVyZUNvb3Jkc19TMCA9IHVub3JtVGV4Q29vcmRzICogdUF0bGFzU2l6ZUludl9TMDsKCXZUZXhJbmRleF9TMCA9IGZsb2F0KHRleElkeCk7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJc2tfUG9zaXRpb24gPSBpblBvc2l0aW9uLnh5MDE7Cn0KAAABAAAAQAMAAHVuaWZvcm0gZmxvYXQ0IHVpbm5lclJlY3RfUzE7CnVuaWZvcm0gaGFsZjIgdXJhZGl1c1BsdXNIYWxmX1MxOwp1bmlmb3JtIHNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMDsKaW4gZmxvYXQyIHZUZXh0dXJlQ29vcmRzX1MwOwpmbGF0IGluIGZsb2F0IHZUZXhJbmRleF9TMDsKaW4gaGFsZjQgdmluQ29sb3JfUzA7CmhhbGY0IENpcmN1bGFyUlJlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MxLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MxLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CgloYWxmIGFscGhhID0gaGFsZihzYXR1cmF0ZSh1cmFkaXVzUGx1c0hhbGZfUzEueCAtIGxlbmd0aChkeHkpKSk7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIEJpdG1hcFRleHQKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWhhbGY0IHRleENvbG9yOwoJewoJCXRleENvbG9yID0gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB2VGV4dHVyZUNvb3Jkc19TMCkucnJycjsKCX0KCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gdGV4Q29sb3I7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBDaXJjdWxhclJSZWN0X1MxKG91dHB1dENvdmVyYWdlX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoBAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAoAAABpblBvc2l0aW9uAAAHAAAAaW5Db2xvcgAPAAAAaW5UZXh0dXJlQ29vcmRzAAAAAAA=","FAAQMYAAMAAAEADAAABAEYAAAICIAB5AABQAAAQAMAAAEATAAABAIIGAAEDCBYQCA4AAAAAAEAB5AAAAACQHEB4XIQAQAADQAAAABAAAAAAABAEMVDOMCJKRAAAAAHAAAAAAAAAAACQAGAAAAAQAAAAAAAQQGAAA":"CgAAAExTS1PUCwAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0NCByYWRpaV9zZWxlY3RvcjsKaW4gZmxvYXQ0IGNvcm5lcl9hbmRfcmFkaXVzX291dHNldHM7CmluIGZsb2F0NCBhYV9ibG9hdF9hbmRfY292ZXJhZ2U7CmluIGZsb2F0NCByYWRpaV94OwppbiBmbG9hdDQgcmFkaWlfeTsKaW4gZmxvYXQ0IHNrZXc7CmluIGZsb2F0MiB0cmFuc2xhdGVfYW5kX2xvY2Fscm90YXRlOwppbiBoYWxmNCBjb2xvcjsKZmxhdCBvdXQgaGFsZjQgdmNvbG9yX1MwOwpvdXQgZmxvYXQyIHZhcmNjb29yZF9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgRmlsbFJSZWN0T3A6OlByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7CglmbG9hdCBhYV9ibG9hdF9tdWx0aXBsaWVyID0gMTsKCWZsb2F0MiBjb3JuZXIgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnh5OwoJZmxvYXQyIHJhZGl1c19vdXRzZXQgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnp3OwoJZmxvYXQyIGFhX2Jsb2F0X2RpcmVjdGlvbiA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS54eTsKCWZsb2F0IGlzX2xpbmVhcl9jb3ZlcmFnZSA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS53OwoJZmxvYXQyIHBpeGVsbGVuZ3RoID0gaW52ZXJzZXNxcnQoZmxvYXQyKGRvdChza2V3Lnh6LCBza2V3Lnh6KSwgZG90KHNrZXcueXcsIHNrZXcueXcpKSk7CglmbG9hdDQgbm9ybWFsaXplZF9heGlzX2RpcnMgPSBza2V3ICogcGl4ZWxsZW5ndGgueHl4eTsKCWZsb2F0MiBheGlzd2lkdGhzID0gKGFicyhub3JtYWxpemVkX2F4aXNfZGlycy54eSkgKyBhYnMobm9ybWFsaXplZF9heGlzX2RpcnMuencpKTsKCWZsb2F0MiBhYV9ibG9hdHJhZGl1cyA9IGF4aXN3aWR0aHMgKiBwaXhlbGxlbmd0aCAqIC41OwoJZmxvYXQ0IHJhZGlpX2FuZF9uZWlnaGJvcnMgPSByYWRpaV9zZWxlY3RvciogZmxvYXQ0eDQocmFkaWlfeCwgcmFkaWlfeSwgcmFkaWlfeC55eHd6LCByYWRpaV95Lnd6eXgpOwoJZmxvYXQyIHJhZGlpID0gcmFkaWlfYW5kX25laWdoYm9ycy54eTsKCWZsb2F0MiBuZWlnaGJvcl9yYWRpaSA9IHJhZGlpX2FuZF9uZWlnaGJvcnMuenc7CglmbG9hdCBjb3ZlcmFnZV9tdWx0aXBsaWVyID0gMTsKCWlmIChhbnkoZ3JlYXRlclRoYW4oYWFfYmxvYXRyYWRpdXMsIGZsb2F0MigxKSkpKSAKCXsKCQljb3JuZXIgPSBtYXgoYWJzKGNvcm5lciksIGFhX2Jsb2F0cmFkaXVzKSAqIHNpZ24oY29ybmVyKTsKCQljb3ZlcmFnZV9tdWx0aXBsaWVyID0gMSAvIChtYXgoYWFfYmxvYXRyYWRpdXMueCwgMSkgKiBtYXgoYWFfYmxvYXRyYWRpdXMueSwgMSkpOwoJCXJhZGlpID0gZmxvYXQyKDApOwoJfQoJZmxvYXQgY292ZXJhZ2UgPSBhYV9ibG9hdF9hbmRfY292ZXJhZ2UuejsKCWlmIChhbnkobGVzc1RoYW4ocmFkaWksIGFhX2Jsb2F0cmFkaXVzICogMS41KSkpIAoJewoJCXJhZGlpID0gZmxvYXQyKDApOwoJCWFhX2Jsb2F0X2RpcmVjdGlvbiA9IHNpZ24oY29ybmVyKTsKCQlpZiAoY292ZXJhZ2UgPiAuNSkgCgkJewoJCQlhYV9ibG9hdF9kaXJlY3Rpb24gPSAtYWFfYmxvYXRfZGlyZWN0aW9uOwoJCX0KCQlpc19saW5lYXJfY292ZXJhZ2UgPSAxOwoJfQoJZWxzZSAKCXsKCQlyYWRpaSA9IGNsYW1wKHJhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQluZWlnaGJvcl9yYWRpaSA9IGNsYW1wKG5laWdoYm9yX3JhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQlmbG9hdDIgc3BhY2luZyA9IDIgLSByYWRpaSAtIG5laWdoYm9yX3JhZGlpOwoJCWZsb2F0MiBleHRyYV9wYWQgPSBtYXgocGl4ZWxsZW5ndGggKiAuMDYyNSAtIHNwYWNpbmcsIGZsb2F0MigwKSk7CgkJcmFkaWkgLT0gZXh0cmFfcGFkICogLjU7Cgl9CglmbG9hdDIgYWFfb3V0c2V0ID0gYWFfYmxvYXRfZGlyZWN0aW9uICogYWFfYmxvYXRyYWRpdXMgKiBhYV9ibG9hdF9tdWx0aXBsaWVyOwoJZmxvYXQyIHZlcnRleHBvcyA9IGNvcm5lciArIHJhZGl1c19vdXRzZXQgKiByYWRpaSArIGFhX291dHNldDsKCWlmIChjb3ZlcmFnZSA+IC41KSAKCXsKCQlpZiAoYWFfYmxvYXRfZGlyZWN0aW9uLnggIT0gMCAmJiB2ZXJ0ZXhwb3MueCAqIGNvcm5lci54IDwgMCkgCgkJewoJCQlmbG9hdCBiYWNrc2V0ID0gYWJzKHZlcnRleHBvcy54KTsKCQkJdmVydGV4cG9zLnggPSAwOwoJCQl2ZXJ0ZXhwb3MueSArPSBiYWNrc2V0ICogc2lnbihjb3JuZXIueSkgKiBwaXhlbGxlbmd0aC55L3BpeGVsbGVuZ3RoLng7CgkJCWNvdmVyYWdlID0gKGNvdmVyYWdlIC0gLjUpICogYWJzKGNvcm5lci54KSAvIChhYnMoY29ybmVyLngpICsgYmFja3NldCkgKyAuNTsKCQl9CgkJaWYgKGFhX2Jsb2F0X2RpcmVjdGlvbi55ICE9IDAgJiYgdmVydGV4cG9zLnkgKiBjb3JuZXIueSA8IDApIAoJCXsKCQkJZmxvYXQgYmFja3NldCA9IGFicyh2ZXJ0ZXhwb3MueSk7CgkJCXZlcnRleHBvcy55ID0gMDsKCQkJdmVydGV4cG9zLnggKz0gYmFja3NldCAqIHNpZ24oY29ybmVyLngpICogcGl4ZWxsZW5ndGgueC9waXhlbGxlbmd0aC55OwoJCQljb3ZlcmFnZSA9IChjb3ZlcmFnZSAtIC41KSAqIGFicyhjb3JuZXIueSkgLyAoYWJzKGNvcm5lci55KSArIGJhY2tzZXQpICsgLjU7CgkJfQoJfQoJZmxvYXQyeDIgc2tld21hdHJpeCA9IGZsb2F0MngyKHNrZXcueHksIHNrZXcuencpOwoJZmxvYXQyIGRldmNvb3JkID0gdmVydGV4cG9zICogc2tld21hdHJpeCArIHRyYW5zbGF0ZV9hbmRfbG9jYWxyb3RhdGUueHk7CglpZiAoMCAhPSBpc19saW5lYXJfY292ZXJhZ2UpIAoJewoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MigwLCBjb3ZlcmFnZSAqIGNvdmVyYWdlX211bHRpcGxpZXIpOwoJfQoJZWxzZSAKCXsKCQlmbG9hdDIgYXJjY29vcmQgPSAxIC0gYWJzKHJhZGl1c19vdXRzZXQpICsgYWFfb3V0c2V0L3JhZGlpICogY29ybmVyOwoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MihhcmNjb29yZC54KzEsIGFyY2Nvb3JkLnkpOwoJfQoJc2tfUG9zaXRpb24gPSBkZXZjb29yZC54eTAxOwp9CgEAAABRBQAAY29uc3QgaW50IGtGaWxsQUFfUzFfYzAgPSAxOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQldfUzFfYzAgPSAyOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQUFfUzFfYzAgPSAzOwp1bmlmb3JtIGZsb2F0NCB1Y2lyY2xlX1MxX2MwOwpmbGF0IGluIGhhbGY0IHZjb2xvcl9TMDsKaW4gZmxvYXQyIHZhcmNjb29yZF9TMDsKaGFsZjQgQ2lyY2xlX1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJaGFsZiBkOwoJaWYgKGludCgzKSA9PSBrSW52ZXJzZUZpbGxCV19TMV9jMCB8fCBpbnQoMykgPT0ga0ludmVyc2VGaWxsQUFfUzFfYzApIAoJewoJCWQgPSBoYWxmKChsZW5ndGgoKHVjaXJjbGVfUzFfYzAueHkgLSBza19GcmFnQ29vcmQueHkpICogdWNpcmNsZV9TMV9jMC53KSAtIDEuMCkgKiB1Y2lyY2xlX1MxX2MwLnopOwoJfQoJZWxzZSAKCXsKCQlkID0gaGFsZigoMS4wIC0gbGVuZ3RoKCh1Y2lyY2xlX1MxX2MwLnh5IC0gc2tfRnJhZ0Nvb3JkLnh5KSAqIHVjaXJjbGVfUzFfYzAudykpICogdWNpcmNsZV9TMV9jMC56KTsKCX0KCXJldHVybiBoYWxmNChoYWxmNChpbnQoMykgPT0ga0ZpbGxBQV9TMV9jMCB8fCBpbnQoMykgPT0ga0ludmVyc2VGaWxsQUFfUzFfYzAgPyBzYXR1cmF0ZShkKSA6IGhhbGYoZCA+IDAuNSA/IDEgOiAwKSkpOwp9CmhhbGY0IEJsZW5kX1MxKGhhbGY0IF9zcmMsIGhhbGY0IF9kc3QpIAp7CglyZXR1cm4gYmxlbmRfbW9kdWxhdGUoX3NyYywgQ2lyY2xlX1MxX2MwKF9zcmMpKTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgRmlsbFJSZWN0T3A6OlByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWZsb2F0IHhfcGx1c18xPXZhcmNjb29yZF9TMC54LCB5PXZhcmNjb29yZF9TMC55OwoJaGFsZiBjb3ZlcmFnZTsKCWlmICgwID09IHhfcGx1c18xKSAKCXsKCQljb3ZlcmFnZSA9IGhhbGYoeSk7Cgl9CgllbHNlIAoJewoJCWZsb2F0IGZuID0geF9wbHVzXzEgKiAoeF9wbHVzXzEgLSAyKTsKCQlmbiA9IGZtYSh5LHksIGZuKTsKCQlmbG9hdCBmbndpZHRoID0gZndpZHRoKGZuKTsKCQljb3ZlcmFnZSA9IC41IC0gaGFsZihmbi9mbndpZHRoKTsKCQljb3ZlcmFnZSA9IGNsYW1wKGNvdmVyYWdlLCAwLCAxKTsKCX0KCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoY292ZXJhZ2UpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gQmxlbmRfUzEob3V0cHV0Q292ZXJhZ2VfUzAsIGhhbGY0KDEpKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoAAAABAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAACAAAAA4AAAByYWRpaV9zZWxlY3RvcgAAGQAAAGNvcm5lcl9hbmRfcmFkaXVzX291dHNldHMAAAAVAAAAYWFfYmxvYXRfYW5kX2NvdmVyYWdlAAAABwAAAHJhZGlpX3gABwAAAHJhZGlpX3kABAAAAHNrZXcZAAAAdHJhbnNsYXRlX2FuZF9sb2NhbHJvdGF0ZQAAAAUAAABjb2xvcgAAAAAAAAA=","AYQA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAACAFBQATAAAAAAFAAMAAAABAAAAAAABBAMAAAAA":"CgAAAExTS1PMAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0NCBpbkNpcmNsZUVkZ2U7Cm91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJdmluQ2lyY2xlRWRnZV9TMCA9IGluQ2lyY2xlRWRnZTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSB1bG9jYWxNYXRyaXhfUzAueHogKiBpblBvc2l0aW9uICsgdWxvY2FsTWF0cml4X1MwLnl3OwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgEAAABABAAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBmbG9hdDIgdWludlJhZGlpWFlfUzE7CmluIGZsb2F0NCB2aW5DaXJjbGVFZGdlX1MwOwppbiBoYWxmNCB2aW5Db2xvcl9TMDsKaGFsZjQgRWxsaXB0aWNhbFJSZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCWZsb2F0MiBkeHkwID0gdWlubmVyUmVjdF9TMS5MVCAtIHNrX0ZyYWdDb29yZC54eTsKCWZsb2F0MiBkeHkxID0gc2tfRnJhZ0Nvb3JkLnh5IC0gdWlubmVyUmVjdF9TMS5SQjsKCWZsb2F0MiBkeHkgPSBtYXgobWF4KGR4eTAsIGR4eTEpLCAwLjApOwoJZmxvYXQyIFogPSBkeHkgKiB1aW52UmFkaWlYWV9TMS54eTsKCWhhbGYgaW1wbGljaXQgPSBoYWxmKGRvdChaLCBkeHkpIC0gMS4wKTsKCWhhbGYgZ3JhZF9kb3QgPSBoYWxmKDQuMCAqIGRvdChaLCBaKSk7CglncmFkX2RvdCA9IG1heChncmFkX2RvdCwgMS4wZS00KTsKCWhhbGYgYXBwcm94X2Rpc3QgPSBpbXBsaWNpdCAqIGhhbGYoaW52ZXJzZXNxcnQoZ3JhZF9kb3QpKTsKCWhhbGYgYWxwaGEgPSBjbGFtcCgwLjUgKyBhcHByb3hfZGlzdCwgMC4wLCAxLjApOwoJcmV0dXJuIF9pbnB1dCAqIGFscGhhOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQ0IGNpcmNsZUVkZ2U7CgljaXJjbGVFZGdlID0gdmluQ2lyY2xlRWRnZV9TMDsKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWZsb2F0IGQgPSBsZW5ndGgoY2lyY2xlRWRnZS54eSk7CgloYWxmIGRpc3RhbmNlVG9PdXRlckVkZ2UgPSBoYWxmKGNpcmNsZUVkZ2UueiAqICgxLjAgLSBkKSk7CgloYWxmIGVkZ2VBbHBoYSA9IHNhdHVyYXRlKGRpc3RhbmNlVG9PdXRlckVkZ2UpOwoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChlZGdlQWxwaGEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gRWxsaXB0aWNhbFJSZWN0X1MxKG91dHB1dENvdmVyYWdlX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoBAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAoAAABpblBvc2l0aW9uAAAHAAAAaW5Db2xvcgAMAAAAaW5DaXJjbGVFZGdlAAAAAA==","HUJAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAQAAAAAAQQGABZAA6IAAAAACAAAAAADUAAAAAAAEAAAAAIDEAAAAAAA":"CgAAAExTS1PlAAAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7Cm91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmxvY2FsQ29vcmRfUzAgPSBsb2NhbENvb3JkOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAAEAAAA7AwAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CnVuaWZvcm0gc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MwOwppbiBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CmhhbGY0IENpcmN1bGFyUlJlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MxLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MxLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CgloYWxmIGFscGhhID0gaGFsZihzYXR1cmF0ZSh1cmFkaXVzUGx1c0hhbGZfUzEueCAtIGxlbmd0aChkeHkpKSk7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzAgPSBoYWxmNCgxKTsKCWZsb2F0MiB0ZXhDb29yZDsKCXRleENvb3JkID0gdmxvY2FsQ29vcmRfUzA7CglvdXRwdXRDb2xvcl9TMCA9IChibGVuZF9tb2R1bGF0ZShzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzAsIHRleENvb3JkKSwgaGFsZjQoMSkpKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBDaXJjdWxhclJSZWN0X1MxKG91dHB1dENvdmVyYWdlX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAIAAAAcG9zaXRpb24KAAAAbG9jYWxDb29yZAAAAAAAAA==","HWQACAAAABAAADAAAIOAAAAADIIAAIRODAAP577774DSAIAA737777YBAAAAAAAAAAAHEADZAAAAAAIAAAAAAOQAAAAAAAQAAAABAMQAAAAAA":"CgAAAExTS1ONAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQgY292ZXJhZ2U7CmluIGhhbGY0IGNvbG9yOwppbiBmbG9hdDQgZ2VvbVN1YnNldDsKZmxhdCBvdXQgaGFsZjQgdmNvbG9yX1MwOwpvdXQgZmxvYXQgdmNvdmVyYWdlX1MwOwpmbGF0IG91dCBmbG9hdDQgdmdlb21TdWJzZXRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQyIHBvc2l0aW9uID0gcG9zaXRpb24ueHk7Cgl2Y29sb3JfUzAgPSBjb2xvcjsKCXZjb3ZlcmFnZV9TMCA9IGNvdmVyYWdlOwoJdmdlb21TdWJzZXRfUzAgPSBnZW9tU3Vic2V0OwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAAEAAAAIBAAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CmZsYXQgaW4gaGFsZjQgdmNvbG9yX1MwOwppbiBmbG9hdCB2Y292ZXJhZ2VfUzA7CmZsYXQgaW4gZmxvYXQ0IHZnZW9tU3Vic2V0X1MwOwpoYWxmNCBDaXJjdWxhclJSZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCWZsb2F0MiBkeHkwID0gdWlubmVyUmVjdF9TMS5MVCAtIHNrX0ZyYWdDb29yZC54eTsKCWZsb2F0MiBkeHkxID0gc2tfRnJhZ0Nvb3JkLnh5IC0gdWlubmVyUmVjdF9TMS5SQjsKCWZsb2F0MiBkeHkgPSBtYXgobWF4KGR4eTAsIGR4eTEpLCAwLjApOwoJaGFsZiBhbHBoYSA9IGhhbGYoc2F0dXJhdGUodXJhZGl1c1BsdXNIYWxmX1MxLnggLSBsZW5ndGgoZHh5KSkpOwoJcmV0dXJuIF9pbnB1dCAqIGFscGhhOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7CglmbG9hdCBjb3ZlcmFnZSA9IHZjb3ZlcmFnZV9TMDsKCWZsb2F0NCBnZW9TdWJzZXQ7CglnZW9TdWJzZXQgPSB2Z2VvbVN1YnNldF9TMDsKCWhhbGY0IGRpc3RzNCA9IGNsYW1wKGhhbGY0KDEsIDEsIC0xLCAtMSkgKiBoYWxmNChza19GcmFnQ29vcmQueHl4eSAtIGdlb1N1YnNldCksIDAsIDEpOwoJaGFsZjIgZGlzdHMyID0gZGlzdHM0Lnh5ICsgZGlzdHM0Lnp3IC0gMTsKCWhhbGYgc3Vic2V0Q292ZXJhZ2UgPSBkaXN0czIueCAqIGRpc3RzMi55OwoJY292ZXJhZ2UgPSBtaW4oY292ZXJhZ2UsIHN1YnNldENvdmVyYWdlKTsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoaGFsZihjb3ZlcmFnZSkpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gQ2lyY3VsYXJSUmVjdF9TMShvdXRwdXRDb3ZlcmFnZV9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzE7Cgl9Cn0KAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAIAAAAcG9zaXRpb24IAAAAY292ZXJhZ2UFAAAAY29sb3IAAAAKAAAAZ2VvbVN1YnNldAAAAAAAAA==","AYQA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAACAAYQADAAAEAFEURUKQKAAAYAAAAAAAAIAAAABSCICWKY2FAEAAAMAAAAAAAAAAAAAIADQAAAAIAAAAAAAIIDAAAA":"CgAAAExTS1PMAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0NCBpbkNpcmNsZUVkZ2U7Cm91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJdmluQ2lyY2xlRWRnZV9TMCA9IGluQ2lyY2xlRWRnZTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSB1bG9jYWxNYXRyaXhfUzAueHogKiBpblBvc2l0aW9uICsgdWxvY2FsTWF0cml4X1MwLnl3OwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgEAAAB+BQAAY29uc3QgaW50IGtGaWxsQldfUzFfYzAgPSAwOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQldfUzFfYzAgPSAyOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQUFfUzFfYzAgPSAzOwp1bmlmb3JtIGZsb2F0NCB1cmVjdFVuaWZvcm1fUzFfYzA7CmluIGZsb2F0NCB2aW5DaXJjbGVFZGdlX1MwOwppbiBoYWxmNCB2aW5Db2xvcl9TMDsKaGFsZjQgUmVjdF9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzBfaW5Db2xvciA9IF9pbnB1dDsKCWhhbGYgY292ZXJhZ2U7CglpZiAoaW50KDEpID09IGtGaWxsQldfUzFfYzAgfHwgaW50KDEpID09IGtJbnZlcnNlRmlsbEJXX1MxX2MwKSAKCXsKCQljb3ZlcmFnZSA9IGhhbGYoYWxsKGdyZWF0ZXJUaGFuKGZsb2F0NChza19GcmFnQ29vcmQueHksIHVyZWN0VW5pZm9ybV9TMV9jMC56dyksIGZsb2F0NCh1cmVjdFVuaWZvcm1fUzFfYzAueHksIHNrX0ZyYWdDb29yZC54eSkpKSk7Cgl9CgllbHNlIAoJewoJCWhhbGY0IGRpc3RzNCA9IHNhdHVyYXRlKGhhbGY0KDEuMCwgMS4wLCAtMS4wLCAtMS4wKSAqIGhhbGY0KHNrX0ZyYWdDb29yZC54eXh5IC0gdXJlY3RVbmlmb3JtX1MxX2MwKSk7CgkJaGFsZjIgZGlzdHMyID0gKGRpc3RzNC54eSArIGRpc3RzNC56dykgLSAxLjA7CgkJY292ZXJhZ2UgPSBkaXN0czIueCAqIGRpc3RzMi55OwoJfQoJaWYgKGludCgxKSA9PSBrSW52ZXJzZUZpbGxCV19TMV9jMCB8fCBpbnQoMSkgPT0ga0ludmVyc2VGaWxsQUFfUzFfYzApIAoJewoJCWNvdmVyYWdlID0gMS4wIC0gY292ZXJhZ2U7Cgl9CglyZXR1cm4gaGFsZjQoaGFsZjQoY292ZXJhZ2UpKTsKfQpoYWxmNCBCbGVuZF9TMShoYWxmNCBfc3JjLCBoYWxmNCBfZHN0KSAKewoJcmV0dXJuIGJsZW5kX21vZHVsYXRlKFJlY3RfUzFfYzAoX3NyYyksIF9zcmMpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQ0IGNpcmNsZUVkZ2U7CgljaXJjbGVFZGdlID0gdmluQ2lyY2xlRWRnZV9TMDsKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWZsb2F0IGQgPSBsZW5ndGgoY2lyY2xlRWRnZS54eSk7CgloYWxmIGRpc3RhbmNlVG9PdXRlckVkZ2UgPSBoYWxmKGNpcmNsZUVkZ2UueiAqICgxLjAgLSBkKSk7CgloYWxmIGVkZ2VBbHBoYSA9IHNhdHVyYXRlKGRpc3RhbmNlVG9PdXRlckVkZ2UpOwoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChlZGdlQWxwaGEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gQmxlbmRfUzEob3V0cHV0Q292ZXJhZ2VfUzAsIGhhbGY0KDEpKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACgAAAGluUG9zaXRpb24AAAcAAABpbkNvbG9yAAwAAABpbkNpcmNsZUVkZ2UAAAAA","BYIBQAAABQAAIAABBYAAAEIXBAAP777777777777AAAAAAAAAAAABUABAAAAAEAAAAAIBEABAAAAA":"CgAAAExTS1M+AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwpvdXQgaGFsZjQgdmNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBEZWZhdWx0R2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IGNvbG9yID0gaW5Db2xvcjsKCXZjb2xvcl9TMCA9IGNvbG9yOwoJZmxvYXQyIF90bXBfMV9pblBvc2l0aW9uID0gaW5Qb3NpdGlvbjsKCWZsb2F0MiBfdG1wXzNfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247Cglza19Qb3NpdGlvbiA9IF90bXBfMV9pblBvc2l0aW9uLnh5MDE7Cn0KAAAAAAAABgEAAGluIGhhbGY0IHZjb2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIERlZmF1bHRHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACgAAAGluUG9zaXRpb24AAAcAAABpbkNvbG9yAAAAAAA=","EADQAAAAAEAAAAAUAABQAAQPAAABCFYMAAKAUEAAAAAAAAABAAAAAAAAAAANAAIAAAABAAAAACAJAAIAAAAA":"CgAAAExTS1NyAgAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQyIHVBdGxhc0RpbWVuc2lvbnNJbnZfUzA7CmluIGZsb2F0MyBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiB1c2hvcnQyIGluVGV4dHVyZUNvb3JkczsKb3V0IGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKZmxhdCBvdXQgZmxvYXQgdlRleEluZGV4X1MwOwpvdXQgZmxvYXQyIHZJbnRUZXh0dXJlQ29vcmRzX1MwOwpvdXQgaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIERpc3RhbmNlRmllbGRQYXRoCglpbnQgdGV4SWR4ID0gMDsKCWZsb2F0MiB1bm9ybVRleENvb3JkcyA9IGZsb2F0MihpblRleHR1cmVDb29yZHMueCwgaW5UZXh0dXJlQ29vcmRzLnkpOwoJdlRleHR1cmVDb29yZHNfUzAgPSB1bm9ybVRleENvb3JkcyAqIHVBdGxhc0RpbWVuc2lvbnNJbnZfUzA7Cgl2VGV4SW5kZXhfUzAgPSBmbG9hdCh0ZXhJZHgpOwoJdkludFRleHR1cmVDb29yZHNfUzAgPSB1bm9ybVRleENvb3JkczsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MyBfdG1wXzFfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247Cglza19Qb3NpdGlvbiA9IGluUG9zaXRpb24ueHkwejsKfQoAAAAAAACfAgAAdW5pZm9ybSBzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzA7CmluIGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKZmxhdCBpbiBmbG9hdCB2VGV4SW5kZXhfUzA7CmluIGZsb2F0MiB2SW50VGV4dHVyZUNvb3Jkc19TMDsKaW4gaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBEaXN0YW5jZUZpZWxkUGF0aAoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZpbkNvbG9yX1MwOwoJZmxvYXQyIHV2ID0gdlRleHR1cmVDb29yZHNfUzA7CgloYWxmNCB0ZXhDb2xvcjsKCXsKCQl0ZXhDb2xvciA9IHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMCwgdXYpLnJycnI7Cgl9CgloYWxmIGRpc3RhbmNlID0gNy45Njg3NSoodGV4Q29sb3IuciAtIDAuNTAxOTYwNzg0MzEpOwoJaGFsZiBhZndpZHRoOwoJYWZ3aWR0aCA9IGFicygwLjY1KmhhbGYoZEZkeCh2SW50VGV4dHVyZUNvb3Jkc19TMC54KSkpOwoJaGFsZiB2YWwgPSBzbW9vdGhzdGVwKC1hZndpZHRoLCBhZndpZHRoLCBkaXN0YW5jZSk7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KHZhbCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADwAAAGluVGV4dHVyZUNvb3JkcwAAAAAA","HTQAAGAABBYAAAEIXBAAAGEAMAAAAAAAAAAAAAAAQAHAAAAAQAAAAAAAQQGAAAAA":"CgAAAExTS1M/AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiBmbG9hdDQgaW5RdWFkRWRnZTsKb3V0IGZsb2F0NCB2UXVhZEVkZ2VfUzA7Cm91dCBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZEVkZ2UKCXZRdWFkRWRnZV9TMCA9IGluUXVhZEVkZ2U7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8wX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgABAAAABQMAAGluIGZsb2F0NCB2UXVhZEVkZ2VfUzA7CmluIGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZEVkZ2UKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWhhbGYgZWRnZUFscGhhOwoJaGFsZjIgZHV2ZHggPSBoYWxmMihkRmR4KHZRdWFkRWRnZV9TMC54eSkpOwoJaGFsZjIgZHV2ZHkgPSBoYWxmMihkRmR5KHZRdWFkRWRnZV9TMC54eSkpOwoJaWYgKHZRdWFkRWRnZV9TMC56ID4gMC4wICYmIHZRdWFkRWRnZV9TMC53ID4gMC4wKSAKCXsKCQllZGdlQWxwaGEgPSBoYWxmKG1pbihtaW4odlF1YWRFZGdlX1MwLnosIHZRdWFkRWRnZV9TMC53KSArIDAuNSwgMS4wKSk7Cgl9CgllbHNlIAoJewoJCWhhbGYyIGdGID0gaGFsZjIoaGFsZigyLjAqdlF1YWRFZGdlX1MwLngqZHV2ZHgueCAtIGR1dmR4LnkpLCAgICAgICAgICAgICAgICAgaGFsZigyLjAqdlF1YWRFZGdlX1MwLngqZHV2ZHkueCAtIGR1dmR5LnkpKTsKCQllZGdlQWxwaGEgPSBoYWxmKHZRdWFkRWRnZV9TMC54KnZRdWFkRWRnZV9TMC54IC0gdlF1YWRFZGdlX1MwLnkpOwoJCWVkZ2VBbHBoYSA9IHNhdHVyYXRlKDAuNSAtIGVkZ2VBbHBoYSAvIGxlbmd0aChnRikpOwoJfQoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChlZGdlQWxwaGEpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IACgAAAGluUXVhZEVkZ2UAAAAAAAA=","HVJAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAABAAAAAABBAMABAAOAAAABAAAAAAABBAMAAA":"CgAAAExTS1MjAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpmbGF0IG91dCBoYWxmNCB2Y29sb3JfUzA7Cm91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7Cgl2bG9jYWxDb29yZF9TMCA9IGxvY2FsQ29vcmQ7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cn0KAAAAAADdAQAAdW5pZm9ybSBzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzA7CmZsYXQgaW4gaGFsZjQgdmNvbG9yX1MwOwppbiBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7CglmbG9hdDIgdGV4Q29vcmQ7Cgl0ZXhDb29yZCA9IHZsb2NhbENvb3JkX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSAoYmxlbmRfbW9kdWxhdGUoc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB0ZXhDb29yZCksIG91dHB1dENvbG9yX1MwKSk7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAIAAAAcG9zaXRpb24FAAAAY29sb3IAAAAKAAAAbG9jYWxDb29yZAAAAAAAAA==","FAAQMYAAMAAAEADAAABAEYAAAICIAB5AABQAAAQAMAAAEATAAABAIIGAAEDCBYQCA4AAAAAAEADZABYAAAIAAAAAACQAGAAAAAQAAAAAAAQQG":"CgAAAExTS1PUCwAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0NCByYWRpaV9zZWxlY3RvcjsKaW4gZmxvYXQ0IGNvcm5lcl9hbmRfcmFkaXVzX291dHNldHM7CmluIGZsb2F0NCBhYV9ibG9hdF9hbmRfY292ZXJhZ2U7CmluIGZsb2F0NCByYWRpaV94OwppbiBmbG9hdDQgcmFkaWlfeTsKaW4gZmxvYXQ0IHNrZXc7CmluIGZsb2F0MiB0cmFuc2xhdGVfYW5kX2xvY2Fscm90YXRlOwppbiBoYWxmNCBjb2xvcjsKZmxhdCBvdXQgaGFsZjQgdmNvbG9yX1MwOwpvdXQgZmxvYXQyIHZhcmNjb29yZF9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgRmlsbFJSZWN0T3A6OlByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7CglmbG9hdCBhYV9ibG9hdF9tdWx0aXBsaWVyID0gMTsKCWZsb2F0MiBjb3JuZXIgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnh5OwoJZmxvYXQyIHJhZGl1c19vdXRzZXQgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnp3OwoJZmxvYXQyIGFhX2Jsb2F0X2RpcmVjdGlvbiA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS54eTsKCWZsb2F0IGlzX2xpbmVhcl9jb3ZlcmFnZSA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS53OwoJZmxvYXQyIHBpeGVsbGVuZ3RoID0gaW52ZXJzZXNxcnQoZmxvYXQyKGRvdChza2V3Lnh6LCBza2V3Lnh6KSwgZG90KHNrZXcueXcsIHNrZXcueXcpKSk7CglmbG9hdDQgbm9ybWFsaXplZF9heGlzX2RpcnMgPSBza2V3ICogcGl4ZWxsZW5ndGgueHl4eTsKCWZsb2F0MiBheGlzd2lkdGhzID0gKGFicyhub3JtYWxpemVkX2F4aXNfZGlycy54eSkgKyBhYnMobm9ybWFsaXplZF9heGlzX2RpcnMuencpKTsKCWZsb2F0MiBhYV9ibG9hdHJhZGl1cyA9IGF4aXN3aWR0aHMgKiBwaXhlbGxlbmd0aCAqIC41OwoJZmxvYXQ0IHJhZGlpX2FuZF9uZWlnaGJvcnMgPSByYWRpaV9zZWxlY3RvciogZmxvYXQ0eDQocmFkaWlfeCwgcmFkaWlfeSwgcmFkaWlfeC55eHd6LCByYWRpaV95Lnd6eXgpOwoJZmxvYXQyIHJhZGlpID0gcmFkaWlfYW5kX25laWdoYm9ycy54eTsKCWZsb2F0MiBuZWlnaGJvcl9yYWRpaSA9IHJhZGlpX2FuZF9uZWlnaGJvcnMuenc7CglmbG9hdCBjb3ZlcmFnZV9tdWx0aXBsaWVyID0gMTsKCWlmIChhbnkoZ3JlYXRlclRoYW4oYWFfYmxvYXRyYWRpdXMsIGZsb2F0MigxKSkpKSAKCXsKCQljb3JuZXIgPSBtYXgoYWJzKGNvcm5lciksIGFhX2Jsb2F0cmFkaXVzKSAqIHNpZ24oY29ybmVyKTsKCQljb3ZlcmFnZV9tdWx0aXBsaWVyID0gMSAvIChtYXgoYWFfYmxvYXRyYWRpdXMueCwgMSkgKiBtYXgoYWFfYmxvYXRyYWRpdXMueSwgMSkpOwoJCXJhZGlpID0gZmxvYXQyKDApOwoJfQoJZmxvYXQgY292ZXJhZ2UgPSBhYV9ibG9hdF9hbmRfY292ZXJhZ2UuejsKCWlmIChhbnkobGVzc1RoYW4ocmFkaWksIGFhX2Jsb2F0cmFkaXVzICogMS41KSkpIAoJewoJCXJhZGlpID0gZmxvYXQyKDApOwoJCWFhX2Jsb2F0X2RpcmVjdGlvbiA9IHNpZ24oY29ybmVyKTsKCQlpZiAoY292ZXJhZ2UgPiAuNSkgCgkJewoJCQlhYV9ibG9hdF9kaXJlY3Rpb24gPSAtYWFfYmxvYXRfZGlyZWN0aW9uOwoJCX0KCQlpc19saW5lYXJfY292ZXJhZ2UgPSAxOwoJfQoJZWxzZSAKCXsKCQlyYWRpaSA9IGNsYW1wKHJhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQluZWlnaGJvcl9yYWRpaSA9IGNsYW1wKG5laWdoYm9yX3JhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQlmbG9hdDIgc3BhY2luZyA9IDIgLSByYWRpaSAtIG5laWdoYm9yX3JhZGlpOwoJCWZsb2F0MiBleHRyYV9wYWQgPSBtYXgocGl4ZWxsZW5ndGggKiAuMDYyNSAtIHNwYWNpbmcsIGZsb2F0MigwKSk7CgkJcmFkaWkgLT0gZXh0cmFfcGFkICogLjU7Cgl9CglmbG9hdDIgYWFfb3V0c2V0ID0gYWFfYmxvYXRfZGlyZWN0aW9uICogYWFfYmxvYXRyYWRpdXMgKiBhYV9ibG9hdF9tdWx0aXBsaWVyOwoJZmxvYXQyIHZlcnRleHBvcyA9IGNvcm5lciArIHJhZGl1c19vdXRzZXQgKiByYWRpaSArIGFhX291dHNldDsKCWlmIChjb3ZlcmFnZSA+IC41KSAKCXsKCQlpZiAoYWFfYmxvYXRfZGlyZWN0aW9uLnggIT0gMCAmJiB2ZXJ0ZXhwb3MueCAqIGNvcm5lci54IDwgMCkgCgkJewoJCQlmbG9hdCBiYWNrc2V0ID0gYWJzKHZlcnRleHBvcy54KTsKCQkJdmVydGV4cG9zLnggPSAwOwoJCQl2ZXJ0ZXhwb3MueSArPSBiYWNrc2V0ICogc2lnbihjb3JuZXIueSkgKiBwaXhlbGxlbmd0aC55L3BpeGVsbGVuZ3RoLng7CgkJCWNvdmVyYWdlID0gKGNvdmVyYWdlIC0gLjUpICogYWJzKGNvcm5lci54KSAvIChhYnMoY29ybmVyLngpICsgYmFja3NldCkgKyAuNTsKCQl9CgkJaWYgKGFhX2Jsb2F0X2RpcmVjdGlvbi55ICE9IDAgJiYgdmVydGV4cG9zLnkgKiBjb3JuZXIueSA8IDApIAoJCXsKCQkJZmxvYXQgYmFja3NldCA9IGFicyh2ZXJ0ZXhwb3MueSk7CgkJCXZlcnRleHBvcy55ID0gMDsKCQkJdmVydGV4cG9zLnggKz0gYmFja3NldCAqIHNpZ24oY29ybmVyLngpICogcGl4ZWxsZW5ndGgueC9waXhlbGxlbmd0aC55OwoJCQljb3ZlcmFnZSA9IChjb3ZlcmFnZSAtIC41KSAqIGFicyhjb3JuZXIueSkgLyAoYWJzKGNvcm5lci55KSArIGJhY2tzZXQpICsgLjU7CgkJfQoJfQoJZmxvYXQyeDIgc2tld21hdHJpeCA9IGZsb2F0MngyKHNrZXcueHksIHNrZXcuencpOwoJZmxvYXQyIGRldmNvb3JkID0gdmVydGV4cG9zICogc2tld21hdHJpeCArIHRyYW5zbGF0ZV9hbmRfbG9jYWxyb3RhdGUueHk7CglpZiAoMCAhPSBpc19saW5lYXJfY292ZXJhZ2UpIAoJewoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MigwLCBjb3ZlcmFnZSAqIGNvdmVyYWdlX211bHRpcGxpZXIpOwoJfQoJZWxzZSAKCXsKCQlmbG9hdDIgYXJjY29vcmQgPSAxIC0gYWJzKHJhZGl1c19vdXRzZXQpICsgYWFfb3V0c2V0L3JhZGlpICogY29ybmVyOwoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MihhcmNjb29yZC54KzEsIGFyY2Nvb3JkLnkpOwoJfQoJc2tfUG9zaXRpb24gPSBkZXZjb29yZC54eTAxOwp9CgEAAADUAwAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CmZsYXQgaW4gaGFsZjQgdmNvbG9yX1MwOwppbiBmbG9hdDIgdmFyY2Nvb3JkX1MwOwpoYWxmNCBDaXJjdWxhclJSZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCWZsb2F0MiBkeHkwID0gdWlubmVyUmVjdF9TMS5MVCAtIHNrX0ZyYWdDb29yZC54eTsKCWZsb2F0MiBkeHkxID0gc2tfRnJhZ0Nvb3JkLnh5IC0gdWlubmVyUmVjdF9TMS5SQjsKCWZsb2F0MiBkeHkgPSBtYXgobWF4KGR4eTAsIGR4eTEpLCAwLjApOwoJaGFsZiBhbHBoYSA9IGhhbGYoc2F0dXJhdGUodXJhZGl1c1BsdXNIYWxmX1MxLnggLSBsZW5ndGgoZHh5KSkpOwoJcmV0dXJuIF9pbnB1dCAqIGFscGhhOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBGaWxsUlJlY3RPcDo6UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJZmxvYXQgeF9wbHVzXzE9dmFyY2Nvb3JkX1MwLngsIHk9dmFyY2Nvb3JkX1MwLnk7CgloYWxmIGNvdmVyYWdlOwoJaWYgKDAgPT0geF9wbHVzXzEpIAoJewoJCWNvdmVyYWdlID0gaGFsZih5KTsKCX0KCWVsc2UgCgl7CgkJZmxvYXQgZm4gPSB4X3BsdXNfMSAqICh4X3BsdXNfMSAtIDIpOwoJCWZuID0gZm1hKHkseSwgZm4pOwoJCWZsb2F0IGZud2lkdGggPSBmd2lkdGgoZm4pOwoJCWNvdmVyYWdlID0gLjUgLSBoYWxmKGZuL2Zud2lkdGgpOwoJCWNvdmVyYWdlID0gY2xhbXAoY292ZXJhZ2UsIDAsIDEpOwoJfQoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChjb3ZlcmFnZSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBDaXJjdWxhclJSZWN0X1MxKG91dHB1dENvdmVyYWdlX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoBAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAACAAAAA4AAAByYWRpaV9zZWxlY3RvcgAAGQAAAGNvcm5lcl9hbmRfcmFkaXVzX291dHNldHMAAAAVAAAAYWFfYmxvYXRfYW5kX2NvdmVyYWdlAAAABwAAAHJhZGlpX3gABwAAAHJhZGlpX3kABAAAAHNrZXcZAAAAdHJhbnNsYXRlX2FuZF9sb2NhbHJvdGF0ZQAAAAUAAABjb2xvcgAAAAAAAAA=","HVIACAAAABQAAGAAAQ4AAAAAGQQAARC4GAAAIOCAAD6P7777777777YDAAAAAAAAAAAFIBQU7BTXIAAAAAACAAAAAAQFV5W6JEAAAAAYAAAABQEZ2AKAWAQAABAL6SYKDYAAAACAAAAAAQEGIAAAAACAWTWL3EYAAAADAAAAACADHIJJCYCAAAEAP2LRIPAAAAAIAAAAAAABTALI3F5SOAIAABQAAAAAABTUEUZMBAAAAAH5FYUXQAAAAAAAEAAAAAZMRGOQCQFQEAAAAAAAAAGARL2LXJHAAEAAAAAEAAAABSCQX5FQUHQAAAAAAAAAACAA4AAAABAACAAAACCAYAAAAA":"CgAAAExTS1PrAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMF9jMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdCBjb3ZlcmFnZTsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpmbGF0IG91dCBoYWxmNCB2Y29sb3JfUzA7Cm91dCBmbG9hdCB2Y292ZXJhZ2VfUzA7Cm91dCBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzZfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQyIHBvc2l0aW9uID0gcG9zaXRpb24ueHk7Cgl2Y29sb3JfUzAgPSBjb2xvcjsKCXZjb3ZlcmFnZV9TMCA9IGNvdmVyYWdlOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwoJewoJCXZUcmFuc2Zvcm1lZENvb3Jkc182X1MwID0gZmxvYXQzeDIodW1hdHJpeF9TMV9jMF9jMF9jMSkgKiBsb2NhbENvb3JkLnh5MTsKCX0KfQoAAAAAAMMHAAB1bmlmb3JtIGhhbGY0IHVzdGFydF9TMV9jMF9jMF9jMDsKdW5pZm9ybSBoYWxmNCB1ZW5kX1MxX2MwX2MwX2MwOwp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzBfYzBfYzE7CnVuaWZvcm0gaGFsZjQgdWxlZnRCb3JkZXJDb2xvcl9TMV9jMF9jMDsKdW5pZm9ybSBoYWxmNCB1cmlnaHRCb3JkZXJDb2xvcl9TMV9jMF9jMDsKZmxhdCBpbiBoYWxmNCB2Y29sb3JfUzA7CmluIGZsb2F0IHZjb3ZlcmFnZV9TMDsKaW4gZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc182X1MwOwpoYWxmNCBTaW5nbGVJbnRlcnZhbENvbG9yaXplcl9TMV9jMF9jMF9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBfY29vcmRzKSAKewoJaGFsZjQgX3RtcF8wX2luQ29sb3IgPSBfaW5wdXQ7CglmbG9hdDIgX3RtcF8xX2Nvb3JkcyA9IF9jb29yZHM7CglyZXR1cm4gaGFsZjQobWl4KHVzdGFydF9TMV9jMF9jMF9jMCwgdWVuZF9TMV9jMF9jMF9jMCwgaGFsZihfdG1wXzFfY29vcmRzLngpKSk7Cn0KaGFsZjQgTGluZWFyTGF5b3V0X1MxX2MwX2MwX2MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMl9pbkNvbG9yID0gX2lucHV0OwoJZmxvYXQyIF90bXBfM19jb29yZHMgPSB2VHJhbnNmb3JtZWRDb29yZHNfNl9TMDsKCXJldHVybiBoYWxmNChoYWxmNChoYWxmKF90bXBfM19jb29yZHMueCkgKyAxZS0wNSwgMS4wLCAwLjAsIDAuMCkpOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMV9jMF9jMF9jMShoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gTGluZWFyTGF5b3V0X1MxX2MwX2MwX2MxX2MwKF9pbnB1dCk7Cn0KaGFsZjQgQ2xhbXBlZEdyYWRpZW50X1MxX2MwX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfNF9pbkNvbG9yID0gX2lucHV0OwoJaGFsZjQgdCA9IE1hdHJpeEVmZmVjdF9TMV9jMF9jMF9jMShfdG1wXzRfaW5Db2xvcik7CgloYWxmNCBvdXRDb2xvcjsKCWlmICghYm9vbChpbnQoMSkpICYmIHQueSA8IDAuMCkgCgl7CgkJb3V0Q29sb3IgPSBoYWxmNCgwLjApOwoJfQoJZWxzZSBpZiAodC54IDwgMC4wKSAKCXsKCQlvdXRDb2xvciA9IHVsZWZ0Qm9yZGVyQ29sb3JfUzFfYzBfYzA7Cgl9CgllbHNlIGlmICh0LnggPiAxLjApIAoJewoJCW91dENvbG9yID0gdXJpZ2h0Qm9yZGVyQ29sb3JfUzFfYzBfYzA7Cgl9CgllbHNlIAoJewoJCW91dENvbG9yID0gU2luZ2xlSW50ZXJ2YWxDb2xvcml6ZXJfUzFfYzBfYzBfYzAoX3RtcF80X2luQ29sb3IsIGZsb2F0MihoYWxmMih0LngsIDAuMCkpKTsKCX0KCXJldHVybiBoYWxmNChvdXRDb2xvcik7Cn0KaGFsZjQgY29sb3JfeGZvcm1fUzFfYzAoZmxvYXQ0IGNvbG9yKSAKewoJY29sb3IucmdiICo9IGNvbG9yLmE7CglyZXR1cm4gaGFsZjQoY29sb3IpOwp9CmhhbGY0IENvbG9yU3BhY2VYZm9ybV9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gY29sb3JfeGZvcm1fUzFfYzAoQ2xhbXBlZEdyYWRpZW50X1MxX2MwX2MwKF9pbnB1dCkpOwp9CmhhbGY0IERpc2FibGVDb3ZlcmFnZUFzQWxwaGFfUzEoaGFsZjQgX2lucHV0KSAKewoJX2lucHV0ID0gQ29sb3JTcGFjZVhmb3JtX1MxX2MwKF9pbnB1dCk7CgloYWxmNCBfdG1wXzVfaW5Db2xvciA9IF9pbnB1dDsKCXJldHVybiBoYWxmNChfaW5wdXQpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7CglmbG9hdCBjb3ZlcmFnZSA9IHZjb3ZlcmFnZV9TMDsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoaGFsZihjb3ZlcmFnZSkpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gRGlzYWJsZUNvdmVyYWdlQXNBbHBoYV9TMShvdXRwdXRDb2xvcl9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gKGhhbGY0KDEuMCkgLSBvdXRwdXRfUzEpICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAHBvc2l0aW9uCAAAAGNvdmVyYWdlBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","DAQAAAAAAABGAABAYAAQAIHCAIAYAQUBAEAAAAAAEAAAAAAAAAAAAIAD2AAAAAAQAVSWGRIBAAADAAAAACAAAAAAQCGEIQOZLBIQAAAABQAAAAAAAAAAAAFAAMAAAABAAAAAAABBAMAAA":"CgAAAExTS1MWAgAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQyIHVBdGxhc1NpemVJbnZfUzA7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiB1c2hvcnQyIGluVGV4dHVyZUNvb3JkczsKb3V0IGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKZmxhdCBvdXQgZmxvYXQgdlRleEluZGV4X1MwOwpvdXQgaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIEJpdG1hcFRleHQKCWludCB0ZXhJZHggPSAwOwoJZmxvYXQyIHVub3JtVGV4Q29vcmRzID0gZmxvYXQyKGluVGV4dHVyZUNvb3Jkcy54LCBpblRleHR1cmVDb29yZHMueSk7Cgl2VGV4dHVyZUNvb3Jkc19TMCA9IHVub3JtVGV4Q29vcmRzICogdUF0bGFzU2l6ZUludl9TMDsKCXZUZXhJbmRleF9TMCA9IGZsb2F0KHRleElkeCk7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJc2tfUG9zaXRpb24gPSBpblBvc2l0aW9uLnh5MDE7Cn0KAAABAAAARQUAAGNvbnN0IGludCBrRmlsbEJXX1MxX2MwID0gMDsKY29uc3QgaW50IGtJbnZlcnNlRmlsbEJXX1MxX2MwID0gMjsKY29uc3QgaW50IGtJbnZlcnNlRmlsbEFBX1MxX2MwID0gMzsKdW5pZm9ybSBmbG9hdDQgdXJlY3RVbmlmb3JtX1MxX2MwOwp1bmlmb3JtIHNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMDsKaW4gZmxvYXQyIHZUZXh0dXJlQ29vcmRzX1MwOwpmbGF0IGluIGZsb2F0IHZUZXhJbmRleF9TMDsKaW4gaGFsZjQgdmluQ29sb3JfUzA7CmhhbGY0IFJlY3RfUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF8wX2luQ29sb3IgPSBfaW5wdXQ7CgloYWxmIGNvdmVyYWdlOwoJaWYgKGludCgxKSA9PSBrRmlsbEJXX1MxX2MwIHx8IGludCgxKSA9PSBrSW52ZXJzZUZpbGxCV19TMV9jMCkgCgl7CgkJY292ZXJhZ2UgPSBoYWxmKGFsbChncmVhdGVyVGhhbihmbG9hdDQoc2tfRnJhZ0Nvb3JkLnh5LCB1cmVjdFVuaWZvcm1fUzFfYzAuencpLCBmbG9hdDQodXJlY3RVbmlmb3JtX1MxX2MwLnh5LCBza19GcmFnQ29vcmQueHkpKSkpOwoJfQoJZWxzZSAKCXsKCQloYWxmNCBkaXN0czQgPSBzYXR1cmF0ZShoYWxmNCgxLjAsIDEuMCwgLTEuMCwgLTEuMCkgKiBoYWxmNChza19GcmFnQ29vcmQueHl4eSAtIHVyZWN0VW5pZm9ybV9TMV9jMCkpOwoJCWhhbGYyIGRpc3RzMiA9IChkaXN0czQueHkgKyBkaXN0czQuencpIC0gMS4wOwoJCWNvdmVyYWdlID0gZGlzdHMyLnggKiBkaXN0czIueTsKCX0KCWlmIChpbnQoMSkgPT0ga0ludmVyc2VGaWxsQldfUzFfYzAgfHwgaW50KDEpID09IGtJbnZlcnNlRmlsbEFBX1MxX2MwKSAKCXsKCQljb3ZlcmFnZSA9IDEuMCAtIGNvdmVyYWdlOwoJfQoJcmV0dXJuIGhhbGY0KGhhbGY0KGNvdmVyYWdlKSk7Cn0KaGFsZjQgQmxlbmRfUzEoaGFsZjQgX3NyYywgaGFsZjQgX2RzdCkgCnsKCXJldHVybiBibGVuZF9tb2R1bGF0ZShSZWN0X1MxX2MwKF9zcmMpLCBfc3JjKTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgQml0bWFwVGV4dAoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZpbkNvbG9yX1MwOwoJaGFsZjQgdGV4Q29sb3I7Cgl7CgkJdGV4Q29sb3IgPSBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzAsIHZUZXh0dXJlQ29vcmRzX1MwKS5ycnJyOwoJfQoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSB0ZXhDb2xvcjsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IEJsZW5kX1MxKG91dHB1dENvdmVyYWdlX1MwLCBoYWxmNCgxKSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzE7Cgl9Cn0KAAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADwAAAGluVGV4dHVyZUNvb3JkcwAAAAAA","AYQQ5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAAAAOQAAAAAAAQAAAABAMQAAAAA":"CgAAAExTS1PMAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0NCBpbkNpcmNsZUVkZ2U7Cm91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJdmluQ2lyY2xlRWRnZV9TMCA9IGluQ2lyY2xlRWRnZTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSB1bG9jYWxNYXRyaXhfUzAueHogKiBpblBvc2l0aW9uICsgdWxvY2FsTWF0cml4X1MwLnl3OwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgAAAAB7AgAAaW4gZmxvYXQ0IHZpbkNpcmNsZUVkZ2VfUzA7CmluIGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgQ2lyY2xlR2VvbWV0cnlQcm9jZXNzb3IKCWZsb2F0NCBjaXJjbGVFZGdlOwoJY2lyY2xlRWRnZSA9IHZpbkNpcmNsZUVkZ2VfUzA7CgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmluQ29sb3JfUzA7CglmbG9hdCBkID0gbGVuZ3RoKGNpcmNsZUVkZ2UueHkpOwoJaGFsZiBkaXN0YW5jZVRvT3V0ZXJFZGdlID0gaGFsZihjaXJjbGVFZGdlLnogKiAoMS4wIC0gZCkpOwoJaGFsZiBlZGdlQWxwaGEgPSBzYXR1cmF0ZShkaXN0YW5jZVRvT3V0ZXJFZGdlKTsKCWhhbGYgZGlzdGFuY2VUb0lubmVyRWRnZSA9IGhhbGYoY2lyY2xlRWRnZS56ICogKGQgLSBjaXJjbGVFZGdlLncpKTsKCWhhbGYgaW5uZXJBbHBoYSA9IHNhdHVyYXRlKGRpc3RhbmNlVG9Jbm5lckVkZ2UpOwoJZWRnZUFscGhhICo9IGlubmVyQWxwaGE7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGVkZ2VBbHBoYSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADAAAAGluQ2lyY2xlRWRnZQAAAAA=","HUJAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAQAAAAAAQQGAAZAADIAAAACU53QJEKAAAAAAMAAAAAIAAAAAAGIRDFB2XASAUAABQAAAAAAAAAAAAADUAAAAAAAEAAAAAIDEAAA":"CgAAAExTS1PlAAAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7Cm91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmxvY2FsQ29vcmRfUzAgPSBsb2NhbENvb3JkOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAAEAAAC4BAAAY29uc3QgaW50IGtGaWxsQUFfUzFfYzAgPSAxOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQldfUzFfYzAgPSAyOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQUFfUzFfYzAgPSAzOwp1bmlmb3JtIGZsb2F0NCB1Y2lyY2xlX1MxX2MwOwp1bmlmb3JtIHNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMDsKaW4gZmxvYXQyIHZsb2NhbENvb3JkX1MwOwpoYWxmNCBDaXJjbGVfUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF8wX2luQ29sb3IgPSBfaW5wdXQ7CgloYWxmIGQ7CglpZiAoaW50KDEpID09IGtJbnZlcnNlRmlsbEJXX1MxX2MwIHx8IGludCgxKSA9PSBrSW52ZXJzZUZpbGxBQV9TMV9jMCkgCgl7CgkJZCA9IGhhbGYoKGxlbmd0aCgodWNpcmNsZV9TMV9jMC54eSAtIHNrX0ZyYWdDb29yZC54eSkgKiB1Y2lyY2xlX1MxX2MwLncpIC0gMS4wKSAqIHVjaXJjbGVfUzFfYzAueik7Cgl9CgllbHNlIAoJewoJCWQgPSBoYWxmKCgxLjAgLSBsZW5ndGgoKHVjaXJjbGVfUzFfYzAueHkgLSBza19GcmFnQ29vcmQueHkpICogdWNpcmNsZV9TMV9jMC53KSkgKiB1Y2lyY2xlX1MxX2MwLnopOwoJfQoJcmV0dXJuIGhhbGY0KGhhbGY0KGludCgxKSA9PSBrRmlsbEFBX1MxX2MwIHx8IGludCgxKSA9PSBrSW52ZXJzZUZpbGxBQV9TMV9jMCA/IHNhdHVyYXRlKGQpIDogaGFsZihkID4gMC41ID8gMSA6IDApKSk7Cn0KaGFsZjQgQmxlbmRfUzEoaGFsZjQgX3NyYywgaGFsZjQgX2RzdCkgCnsKCXJldHVybiBibGVuZF9tb2R1bGF0ZShfc3JjLCBDaXJjbGVfUzFfYzAoX3NyYykpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwID0gaGFsZjQoMSk7CglmbG9hdDIgdGV4Q29vcmQ7Cgl0ZXhDb29yZCA9IHZsb2NhbENvb3JkX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSAoYmxlbmRfbW9kdWxhdGUoc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB0ZXhDb29yZCksIGhhbGY0KDEpKSk7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gQmxlbmRfUzEob3V0cHV0Q292ZXJhZ2VfUzAsIGhhbGY0KDEpKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoBAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgoAAABsb2NhbENvb3JkAAAAAAAA","AYQA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAAAAOQAAAAAAAQAAAABAMQAAAAA":"CgAAAExTS1PMAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0NCBpbkNpcmNsZUVkZ2U7Cm91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJdmluQ2lyY2xlRWRnZV9TMCA9IGluQ2lyY2xlRWRnZTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSB1bG9jYWxNYXRyaXhfUzAueHogKiBpblBvc2l0aW9uICsgdWxvY2FsTWF0cml4X1MwLnl3OwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgAAAADqAQAAaW4gZmxvYXQ0IHZpbkNpcmNsZUVkZ2VfUzA7CmluIGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgQ2lyY2xlR2VvbWV0cnlQcm9jZXNzb3IKCWZsb2F0NCBjaXJjbGVFZGdlOwoJY2lyY2xlRWRnZSA9IHZpbkNpcmNsZUVkZ2VfUzA7CgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmluQ29sb3JfUzA7CglmbG9hdCBkID0gbGVuZ3RoKGNpcmNsZUVkZ2UueHkpOwoJaGFsZiBkaXN0YW5jZVRvT3V0ZXJFZGdlID0gaGFsZihjaXJjbGVFZGdlLnogKiAoMS4wIC0gZCkpOwoJaGFsZiBlZGdlQWxwaGEgPSBzYXR1cmF0ZShkaXN0YW5jZVRvT3V0ZXJFZGdlKTsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoZWRnZUFscGhhKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADAAAAGluQ2lyY2xlRWRnZQAAAAA=","HUIAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAAAAAALUAQDAAAAAAAAAEAAAABJGQAAAAAAAAIAAAAAWCBAAAABAAAAANAECAZAAAAAAAAAAAFAAMAAAABAAAAAAABBAM":"CgAAAExTS1M2AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAAAAO4EAAB1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzBfYzA7CnVuaWZvcm0gaGFsZjIgdUluY3JlbWVudF9TMV9jMDsKdW5pZm9ybSBoYWxmMiB1T2Zmc2V0c0FuZEtlcm5lbF9TMV9jMFs0XTsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxOwp1bmlmb3JtIHNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMTsKaW4gZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwpoYWxmNCBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwX2MwKGhhbGY0IF9pbnB1dCwgZmxvYXQyIF9jb29yZHMpIAp7CglyZXR1cm4gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MxLCBfY29vcmRzKTsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzFfYzBfYzAoaGFsZjQgX2lucHV0LCBmbG9hdDIgX2Nvb3JkcykgCnsKCXJldHVybiBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwX2MwKF9pbnB1dCwgZmxvYXQzeDIodW1hdHJpeF9TMV9jMF9jMCkgKiBfY29vcmRzLnh5MSk7Cn0KaGFsZjQgU21vb3RoX1MxX2MwKGhhbGY0IF9pbnB1dCwgZmxvYXQyIGNvb3JkLCBoYWxmMiBvZmZzZXRBbmRLZXJuZWwpIAp7CglyZXR1cm4gTWF0cml4RWZmZWN0X1MxX2MwX2MwKF9pbnB1dCwgKGNvb3JkICsgb2Zmc2V0QW5kS2VybmVsLnggKiB1SW5jcmVtZW50X1MxX2MwKSkgKiBvZmZzZXRBbmRLZXJuZWwueTsKfQpoYWxmNCBHYXVzc2lhbkNvbnZvbHV0aW9uX1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IGNvbG9yID0gaGFsZjQoMCk7CglmbG9hdDIgY29vcmQgPSB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKCWZvciAoaW50IGk9MDsgaTw0OyArK2kpIAoJewoJCWNvbG9yICs9IFNtb290aF9TMV9jMChfaW5wdXQsIGNvb3JkLCB1T2Zmc2V0c0FuZEtlcm5lbF9TMV9jMFtpXSk7Cgl9CglyZXR1cm4gY29sb3I7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBHYXVzc2lhbkNvbnZvbHV0aW9uX1MxX2MwKF9pbnB1dCk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzAgPSBoYWxmNCgxKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBNYXRyaXhFZmZlY3RfUzEob3V0cHV0Q29sb3JfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAIAAAAcG9zaXRpb24KAAAAbG9jYWxDb29yZAAAAAAAAA==","HUIAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAAAAAADUAANAAAAAAIBAIAAAABLCIIBAAAAABAEGABBAMAACAIAAAAAAAB2AAAAAAACAAAAAEBSAAAAA":"CgAAAExTS1M8AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMDsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzNfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxX2MwKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAADJAwAAdW5pZm9ybSBmbG9hdDQgdWNsYW1wX1MxX2MwX2MwOwp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzA7CnVuaWZvcm0gc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MxOwppbiBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzNfUzA7CmhhbGY0IFRleHR1cmVFZmZlY3RfUzFfYzBfYzAoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGluQ29vcmQgPSB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKCWZsb2F0MiBzdWJzZXRDb29yZDsKCXN1YnNldENvb3JkLnggPSBpbkNvb3JkLng7CglzdWJzZXRDb29yZC55ID0gaW5Db29yZC55OwoJZmxvYXQyIGNsYW1wZWRDb29yZDsKCWNsYW1wZWRDb29yZCA9IGNsYW1wKHN1YnNldENvb3JkLCB1Y2xhbXBfUzFfYzBfYzAueHksIHVjbGFtcF9TMV9jMF9jMC56dyk7CgloYWxmNCB0ZXh0dXJlQ29sb3IgPSBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzEsIGNsYW1wZWRDb29yZCk7CglyZXR1cm4gdGV4dHVyZUNvbG9yOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gVGV4dHVyZUVmZmVjdF9TMV9jMF9jMChfaW5wdXQpOwp9CmhhbGY0IEJsZW5kX1MxKGhhbGY0IF9zcmMsIGhhbGY0IF9kc3QpIAp7CglyZXR1cm4gYmxlbmRfbW9kdWxhdGUoTWF0cml4RWZmZWN0X1MxX2MwKF9zcmMpLCBfc3JjKTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMCA9IGhhbGY0KDEpOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IEJsZW5kX1MxKG91dHB1dENvbG9yX1MwLCBoYWxmNCgxKSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0X1MxICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAIAAAAcG9zaXRpb24KAAAAbG9jYWxDb29yZAAAAAAAAA==","HUQAAAAAAAMAADAAAIOAAAH677776IZOCAAP577777777777777777YBAAAAAAAAAAADEAANAAAAALHCKLMRAAAAAAAAABAAAAAGJBCFLQVBWAQAAAAAAQAAAAAMACQCAACAAAAA2AIBAEIAAAAAAAAAAAAIADQAAAAIAAAAAAAIIDAAAAAA":"CgAAAExTS1PUAAAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmZsYXQgb3V0IGhhbGY0IHZjb2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKfQoBAAAAeAQAAHVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMDsKdW5pZm9ybSBoYWxmNCB1Y2lyY2xlRGF0YV9TMV9jMDsKdW5pZm9ybSBzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7CmZsYXQgaW4gaGFsZjQgdmNvbG9yX1MwOwpoYWxmNCBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwX2MwKGhhbGY0IF9pbnB1dCwgZmxvYXQyIF9jb29yZHMpIAp7CglyZXR1cm4gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MxLCBfY29vcmRzKS4wMDByOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMV9jMF9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBfY29vcmRzKSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzBfYzBfYzAoX2lucHV0LCBmbG9hdDN4Mih1bWF0cml4X1MxX2MwX2MwKSAqIF9jb29yZHMueHkxKTsKfQpoYWxmNCBDaXJjbGVCbHVyX1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJaGFsZjIgdmVjID0gaGFsZjIoKHNrX0ZyYWdDb29yZC54eSAtIGZsb2F0Mih1Y2lyY2xlRGF0YV9TMV9jMC54eSkpICogZmxvYXQodWNpcmNsZURhdGFfUzFfYzAudykpOwoJaGFsZiBkaXN0ID0gbGVuZ3RoKHZlYykgKyAoMC41IC0gdWNpcmNsZURhdGFfUzFfYzAueikgKiB1Y2lyY2xlRGF0YV9TMV9jMC53OwoJcmV0dXJuIGhhbGY0KE1hdHJpeEVmZmVjdF9TMV9jMF9jMChfdG1wXzBfaW5Db2xvciwgZmxvYXQyKGhhbGYyKGRpc3QsIDAuNSkpKS53d3d3KTsKfQpoYWxmNCBCbGVuZF9TMShoYWxmNCBfc3JjLCBoYWxmNCBfZHN0KSAKewoJcmV0dXJuIGJsZW5kX21vZHVsYXRlKENpcmNsZUJsdXJfUzFfYzAoX3NyYyksIF9zcmMpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gQmxlbmRfUzEob3V0cHV0Q292ZXJhZ2VfUzAsIGhhbGY0KDEpKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoBAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgUAAABjb2xvcgAAAAAAAAA=","HWQACAAAABAAADAAAIOAAAAADIIAAIRODAAP577774DSAIAA737777YBAAAAAAAAAAAKAAYAAAACAAAAAAACCAYAAA":"CgAAAExTS1ONAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQgY292ZXJhZ2U7CmluIGhhbGY0IGNvbG9yOwppbiBmbG9hdDQgZ2VvbVN1YnNldDsKZmxhdCBvdXQgaGFsZjQgdmNvbG9yX1MwOwpvdXQgZmxvYXQgdmNvdmVyYWdlX1MwOwpmbGF0IG91dCBmbG9hdDQgdmdlb21TdWJzZXRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQyIHBvc2l0aW9uID0gcG9zaXRpb24ueHk7Cgl2Y29sb3JfUzAgPSBjb2xvcjsKCXZjb3ZlcmFnZV9TMCA9IGNvdmVyYWdlOwoJdmdlb21TdWJzZXRfUzAgPSBnZW9tU3Vic2V0OwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAAEAAAB5AgAAZmxhdCBpbiBoYWxmNCB2Y29sb3JfUzA7CmluIGZsb2F0IHZjb3ZlcmFnZV9TMDsKZmxhdCBpbiBmbG9hdDQgdmdlb21TdWJzZXRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7CglmbG9hdCBjb3ZlcmFnZSA9IHZjb3ZlcmFnZV9TMDsKCWZsb2F0NCBnZW9TdWJzZXQ7CglnZW9TdWJzZXQgPSB2Z2VvbVN1YnNldF9TMDsKCWhhbGY0IGRpc3RzNCA9IGNsYW1wKGhhbGY0KDEsIDEsIC0xLCAtMSkgKiBoYWxmNChza19GcmFnQ29vcmQueHl4eSAtIGdlb1N1YnNldCksIDAsIDEpOwoJaGFsZjIgZGlzdHMyID0gZGlzdHM0Lnh5ICsgZGlzdHM0Lnp3IC0gMTsKCWhhbGYgc3Vic2V0Q292ZXJhZ2UgPSBkaXN0czIueCAqIGRpc3RzMi55OwoJY292ZXJhZ2UgPSBtaW4oY292ZXJhZ2UsIHN1YnNldENvdmVyYWdlKTsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoaGFsZihjb3ZlcmFnZSkpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAIAAAAcG9zaXRpb24IAAAAY292ZXJhZ2UFAAAAY29sb3IAAAAKAAAAZ2VvbVN1YnNldAAAAAAAAA==","HUQAAAAAAAMAADAAAIOAAAH677776IZOCAAP577777777777777777YBAAAAAAAAAAAKAAYAAAACAAAAAAACCAYAAA":"CgAAAExTS1PUAAAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmZsYXQgb3V0IGhhbGY0IHZjb2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKfQoAAAAAEQEAAGZsYXQgaW4gaGFsZjQgdmNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAAAAAAAA==","HUQACAAAAAMAADAAAIOAAAH677776IZOCAAP577777777777777777YBAAAAAAAAAAAHEADZAAAAAAIAAAAAAOQAAAAAAAQAAAABAMQAAAAAA":"CgAAAExTS1PPAAAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7Cm91dCBoYWxmNCB2Y29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cn0KAAEAAACbAgAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CmluIGhhbGY0IHZjb2xvcl9TMDsKaGFsZjQgQ2lyY3VsYXJSUmVjdF9TMShoYWxmNCBfaW5wdXQpIAp7CglmbG9hdDIgZHh5MCA9IHVpbm5lclJlY3RfUzEuTFQgLSBza19GcmFnQ29vcmQueHk7CglmbG9hdDIgZHh5MSA9IHNrX0ZyYWdDb29yZC54eSAtIHVpbm5lclJlY3RfUzEuUkI7CglmbG9hdDIgZHh5ID0gbWF4KG1heChkeHkwLCBkeHkxKSwgMC4wKTsKCWhhbGYgYWxwaGEgPSBoYWxmKHNhdHVyYXRlKHVyYWRpdXNQbHVzSGFsZl9TMS54IC0gbGVuZ3RoKGR4eSkpKTsKCXJldHVybiBfaW5wdXQgKiBhbHBoYTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IENpcmN1bGFyUlJlY3RfUzEob3V0cHV0Q292ZXJhZ2VfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0X1MxOwoJfQp9CgABAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgUAAABjb2xvcgAAAAAAAAA=","FAAQMYAAMAAAEADAAABAEYAAAICIAB5AABQAAAQAMAAAEATAAABAIIGAAEDCBYQCA4AAAAAAAA5AAAAAAABAAAAACAZAAAAA":"CgAAAExTS1PUCwAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0NCByYWRpaV9zZWxlY3RvcjsKaW4gZmxvYXQ0IGNvcm5lcl9hbmRfcmFkaXVzX291dHNldHM7CmluIGZsb2F0NCBhYV9ibG9hdF9hbmRfY292ZXJhZ2U7CmluIGZsb2F0NCByYWRpaV94OwppbiBmbG9hdDQgcmFkaWlfeTsKaW4gZmxvYXQ0IHNrZXc7CmluIGZsb2F0MiB0cmFuc2xhdGVfYW5kX2xvY2Fscm90YXRlOwppbiBoYWxmNCBjb2xvcjsKZmxhdCBvdXQgaGFsZjQgdmNvbG9yX1MwOwpvdXQgZmxvYXQyIHZhcmNjb29yZF9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgRmlsbFJSZWN0T3A6OlByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7CglmbG9hdCBhYV9ibG9hdF9tdWx0aXBsaWVyID0gMTsKCWZsb2F0MiBjb3JuZXIgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnh5OwoJZmxvYXQyIHJhZGl1c19vdXRzZXQgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnp3OwoJZmxvYXQyIGFhX2Jsb2F0X2RpcmVjdGlvbiA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS54eTsKCWZsb2F0IGlzX2xpbmVhcl9jb3ZlcmFnZSA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS53OwoJZmxvYXQyIHBpeGVsbGVuZ3RoID0gaW52ZXJzZXNxcnQoZmxvYXQyKGRvdChza2V3Lnh6LCBza2V3Lnh6KSwgZG90KHNrZXcueXcsIHNrZXcueXcpKSk7CglmbG9hdDQgbm9ybWFsaXplZF9heGlzX2RpcnMgPSBza2V3ICogcGl4ZWxsZW5ndGgueHl4eTsKCWZsb2F0MiBheGlzd2lkdGhzID0gKGFicyhub3JtYWxpemVkX2F4aXNfZGlycy54eSkgKyBhYnMobm9ybWFsaXplZF9heGlzX2RpcnMuencpKTsKCWZsb2F0MiBhYV9ibG9hdHJhZGl1cyA9IGF4aXN3aWR0aHMgKiBwaXhlbGxlbmd0aCAqIC41OwoJZmxvYXQ0IHJhZGlpX2FuZF9uZWlnaGJvcnMgPSByYWRpaV9zZWxlY3RvciogZmxvYXQ0eDQocmFkaWlfeCwgcmFkaWlfeSwgcmFkaWlfeC55eHd6LCByYWRpaV95Lnd6eXgpOwoJZmxvYXQyIHJhZGlpID0gcmFkaWlfYW5kX25laWdoYm9ycy54eTsKCWZsb2F0MiBuZWlnaGJvcl9yYWRpaSA9IHJhZGlpX2FuZF9uZWlnaGJvcnMuenc7CglmbG9hdCBjb3ZlcmFnZV9tdWx0aXBsaWVyID0gMTsKCWlmIChhbnkoZ3JlYXRlclRoYW4oYWFfYmxvYXRyYWRpdXMsIGZsb2F0MigxKSkpKSAKCXsKCQljb3JuZXIgPSBtYXgoYWJzKGNvcm5lciksIGFhX2Jsb2F0cmFkaXVzKSAqIHNpZ24oY29ybmVyKTsKCQljb3ZlcmFnZV9tdWx0aXBsaWVyID0gMSAvIChtYXgoYWFfYmxvYXRyYWRpdXMueCwgMSkgKiBtYXgoYWFfYmxvYXRyYWRpdXMueSwgMSkpOwoJCXJhZGlpID0gZmxvYXQyKDApOwoJfQoJZmxvYXQgY292ZXJhZ2UgPSBhYV9ibG9hdF9hbmRfY292ZXJhZ2UuejsKCWlmIChhbnkobGVzc1RoYW4ocmFkaWksIGFhX2Jsb2F0cmFkaXVzICogMS41KSkpIAoJewoJCXJhZGlpID0gZmxvYXQyKDApOwoJCWFhX2Jsb2F0X2RpcmVjdGlvbiA9IHNpZ24oY29ybmVyKTsKCQlpZiAoY292ZXJhZ2UgPiAuNSkgCgkJewoJCQlhYV9ibG9hdF9kaXJlY3Rpb24gPSAtYWFfYmxvYXRfZGlyZWN0aW9uOwoJCX0KCQlpc19saW5lYXJfY292ZXJhZ2UgPSAxOwoJfQoJZWxzZSAKCXsKCQlyYWRpaSA9IGNsYW1wKHJhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQluZWlnaGJvcl9yYWRpaSA9IGNsYW1wKG5laWdoYm9yX3JhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQlmbG9hdDIgc3BhY2luZyA9IDIgLSByYWRpaSAtIG5laWdoYm9yX3JhZGlpOwoJCWZsb2F0MiBleHRyYV9wYWQgPSBtYXgocGl4ZWxsZW5ndGggKiAuMDYyNSAtIHNwYWNpbmcsIGZsb2F0MigwKSk7CgkJcmFkaWkgLT0gZXh0cmFfcGFkICogLjU7Cgl9CglmbG9hdDIgYWFfb3V0c2V0ID0gYWFfYmxvYXRfZGlyZWN0aW9uICogYWFfYmxvYXRyYWRpdXMgKiBhYV9ibG9hdF9tdWx0aXBsaWVyOwoJZmxvYXQyIHZlcnRleHBvcyA9IGNvcm5lciArIHJhZGl1c19vdXRzZXQgKiByYWRpaSArIGFhX291dHNldDsKCWlmIChjb3ZlcmFnZSA+IC41KSAKCXsKCQlpZiAoYWFfYmxvYXRfZGlyZWN0aW9uLnggIT0gMCAmJiB2ZXJ0ZXhwb3MueCAqIGNvcm5lci54IDwgMCkgCgkJewoJCQlmbG9hdCBiYWNrc2V0ID0gYWJzKHZlcnRleHBvcy54KTsKCQkJdmVydGV4cG9zLnggPSAwOwoJCQl2ZXJ0ZXhwb3MueSArPSBiYWNrc2V0ICogc2lnbihjb3JuZXIueSkgKiBwaXhlbGxlbmd0aC55L3BpeGVsbGVuZ3RoLng7CgkJCWNvdmVyYWdlID0gKGNvdmVyYWdlIC0gLjUpICogYWJzKGNvcm5lci54KSAvIChhYnMoY29ybmVyLngpICsgYmFja3NldCkgKyAuNTsKCQl9CgkJaWYgKGFhX2Jsb2F0X2RpcmVjdGlvbi55ICE9IDAgJiYgdmVydGV4cG9zLnkgKiBjb3JuZXIueSA8IDApIAoJCXsKCQkJZmxvYXQgYmFja3NldCA9IGFicyh2ZXJ0ZXhwb3MueSk7CgkJCXZlcnRleHBvcy55ID0gMDsKCQkJdmVydGV4cG9zLnggKz0gYmFja3NldCAqIHNpZ24oY29ybmVyLngpICogcGl4ZWxsZW5ndGgueC9waXhlbGxlbmd0aC55OwoJCQljb3ZlcmFnZSA9IChjb3ZlcmFnZSAtIC41KSAqIGFicyhjb3JuZXIueSkgLyAoYWJzKGNvcm5lci55KSArIGJhY2tzZXQpICsgLjU7CgkJfQoJfQoJZmxvYXQyeDIgc2tld21hdHJpeCA9IGZsb2F0MngyKHNrZXcueHksIHNrZXcuencpOwoJZmxvYXQyIGRldmNvb3JkID0gdmVydGV4cG9zICogc2tld21hdHJpeCArIHRyYW5zbGF0ZV9hbmRfbG9jYWxyb3RhdGUueHk7CglpZiAoMCAhPSBpc19saW5lYXJfY292ZXJhZ2UpIAoJewoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MigwLCBjb3ZlcmFnZSAqIGNvdmVyYWdlX211bHRpcGxpZXIpOwoJfQoJZWxzZSAKCXsKCQlmbG9hdDIgYXJjY29vcmQgPSAxIC0gYWJzKHJhZGl1c19vdXRzZXQpICsgYWFfb3V0c2V0L3JhZGlpICogY29ybmVyOwoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MihhcmNjb29yZC54KzEsIGFyY2Nvb3JkLnkpOwoJfQoJc2tfUG9zaXRpb24gPSBkZXZjb29yZC54eTAxOwp9CgAAAABFAgAAZmxhdCBpbiBoYWxmNCB2Y29sb3JfUzA7CmluIGZsb2F0MiB2YXJjY29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBGaWxsUlJlY3RPcDo6UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJZmxvYXQgeF9wbHVzXzE9dmFyY2Nvb3JkX1MwLngsIHk9dmFyY2Nvb3JkX1MwLnk7CgloYWxmIGNvdmVyYWdlOwoJaWYgKDAgPT0geF9wbHVzXzEpIAoJewoJCWNvdmVyYWdlID0gaGFsZih5KTsKCX0KCWVsc2UgCgl7CgkJZmxvYXQgZm4gPSB4X3BsdXNfMSAqICh4X3BsdXNfMSAtIDIpOwoJCWZuID0gZm1hKHkseSwgZm4pOwoJCWZsb2F0IGZud2lkdGggPSBmd2lkdGgoZm4pOwoJCWNvdmVyYWdlID0gLjUgLSBoYWxmKGZuL2Zud2lkdGgpOwoJCWNvdmVyYWdlID0gY2xhbXAoY292ZXJhZ2UsIDAsIDEpOwoJfQoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChjb3ZlcmFnZSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAACAAAAA4AAAByYWRpaV9zZWxlY3RvcgAAGQAAAGNvcm5lcl9hbmRfcmFkaXVzX291dHNldHMAAAAVAAAAYWFfYmxvYXRfYW5kX2NvdmVyYWdlAAAABwAAAHJhZGlpX3gABwAAAHJhZGlpX3kABAAAAHNrZXcZAAAAdHJhbnNsYXRlX2FuZF9sb2NhbHJvdGF0ZQAAAAUAAABjb2xvcgAAAAAAAAA=","HUIAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAAAAAALUAQMAAAAAAIAAEAAAABJYQAAAAAQAAIAAAAAWCBACAABAAAAANAECAZAAEAAAAAAAAFAAMAAAABAAAAAAABBAM":"CgAAAExTS1M2AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAAAAC8GAAB1bmlmb3JtIGZsb2F0NCB1Y2xhbXBfUzFfYzBfYzBfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMDsKdW5pZm9ybSBoYWxmMiB1SW5jcmVtZW50X1MxX2MwOwp1bmlmb3JtIGhhbGYyIHVPZmZzZXRzQW5kS2VybmVsX1MxX2MwWzEzXTsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxOwp1bmlmb3JtIHNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMTsKaW4gZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwpoYWxmNCBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwX2MwKGhhbGY0IF9pbnB1dCwgZmxvYXQyIF9jb29yZHMpIAp7CglmbG9hdDIgaW5Db29yZCA9IF9jb29yZHM7CglmbG9hdDIgc3Vic2V0Q29vcmQ7CglzdWJzZXRDb29yZC54ID0gaW5Db29yZC54OwoJc3Vic2V0Q29vcmQueSA9IGluQ29vcmQueTsKCWZsb2F0MiBjbGFtcGVkQ29vcmQ7CgljbGFtcGVkQ29vcmQueCA9IGNsYW1wKHN1YnNldENvb3JkLngsIHVjbGFtcF9TMV9jMF9jMF9jMC54LCB1Y2xhbXBfUzFfYzBfYzBfYzAueik7CgljbGFtcGVkQ29vcmQueSA9IHN1YnNldENvb3JkLnk7CgloYWxmNCB0ZXh0dXJlQ29sb3IgPSBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzEsIGNsYW1wZWRDb29yZCk7CglyZXR1cm4gdGV4dHVyZUNvbG9yOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMV9jMF9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBfY29vcmRzKSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzBfYzBfYzAoX2lucHV0LCBmbG9hdDN4Mih1bWF0cml4X1MxX2MwX2MwKSAqIF9jb29yZHMueHkxKTsKfQpoYWxmNCBTbW9vdGhfUzFfYzAoaGFsZjQgX2lucHV0LCBmbG9hdDIgY29vcmQsIGhhbGYyIG9mZnNldEFuZEtlcm5lbCkgCnsKCXJldHVybiBNYXRyaXhFZmZlY3RfUzFfYzBfYzAoX2lucHV0LCAoY29vcmQgKyBvZmZzZXRBbmRLZXJuZWwueCAqIHVJbmNyZW1lbnRfUzFfYzApKSAqIG9mZnNldEFuZEtlcm5lbC55Owp9CmhhbGY0IEdhdXNzaWFuQ29udm9sdXRpb25fUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgY29sb3IgPSBoYWxmNCgwKTsKCWZsb2F0MiBjb29yZCA9IHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwoJZm9yIChpbnQgaT0wOyBpPDEzOyArK2kpIAoJewoJCWNvbG9yICs9IFNtb290aF9TMV9jMChfaW5wdXQsIGNvb3JkLCB1T2Zmc2V0c0FuZEtlcm5lbF9TMV9jMFtpXSk7Cgl9CglyZXR1cm4gY29sb3I7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBHYXVzc2lhbkNvbnZvbHV0aW9uX1MxX2MwKF9pbnB1dCk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzAgPSBoYWxmNCgxKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBNYXRyaXhFZmZlY3RfUzEob3V0cHV0Q29sb3JfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgoAAABsb2NhbENvb3JkAAAAAAAA","HUIAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAAAAAALUAQMAAAAAAAAAEAAAABJYQAAAAAAAAIAAAAAWCBAAAABAAAAANAECAZAAAAAAAAAAAFAAMAAAABAAAAAAABBAM":"CgAAAExTS1M2AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAAAAPAEAAB1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzBfYzA7CnVuaWZvcm0gaGFsZjIgdUluY3JlbWVudF9TMV9jMDsKdW5pZm9ybSBoYWxmMiB1T2Zmc2V0c0FuZEtlcm5lbF9TMV9jMFsxM107CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKdW5pZm9ybSBzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7CmluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMF9jMF9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBfY29vcmRzKSAKewoJcmV0dXJuIHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMSwgX2Nvb3Jkcyk7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxX2MwX2MwKGhhbGY0IF9pbnB1dCwgZmxvYXQyIF9jb29yZHMpIAp7CglyZXR1cm4gVGV4dHVyZUVmZmVjdF9TMV9jMF9jMF9jMChfaW5wdXQsIGZsb2F0M3gyKHVtYXRyaXhfUzFfYzBfYzApICogX2Nvb3Jkcy54eTEpOwp9CmhhbGY0IFNtb290aF9TMV9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBjb29yZCwgaGFsZjIgb2Zmc2V0QW5kS2VybmVsKSAKewoJcmV0dXJuIE1hdHJpeEVmZmVjdF9TMV9jMF9jMChfaW5wdXQsIChjb29yZCArIG9mZnNldEFuZEtlcm5lbC54ICogdUluY3JlbWVudF9TMV9jMCkpICogb2Zmc2V0QW5kS2VybmVsLnk7Cn0KaGFsZjQgR2F1c3NpYW5Db252b2x1dGlvbl9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBjb2xvciA9IGhhbGY0KDApOwoJZmxvYXQyIGNvb3JkID0gdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7Cglmb3IgKGludCBpPTA7IGk8MTM7ICsraSkgCgl7CgkJY29sb3IgKz0gU21vb3RoX1MxX2MwKF9pbnB1dCwgY29vcmQsIHVPZmZzZXRzQW5kS2VybmVsX1MxX2MwW2ldKTsKCX0KCXJldHVybiBjb2xvcjsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIEdhdXNzaWFuQ29udm9sdXRpb25fUzFfYzAoX2lucHV0KTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMCA9IGhhbGY0KDEpOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IE1hdHJpeEVmZmVjdF9TMShvdXRwdXRDb2xvcl9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0X1MxICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAIAAAAcG9zaXRpb24KAAAAbG9jYWxDb29yZAAAAAAAAA==","HVIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAGIBIAAABAAAAANAEAAAAAAAAAAAAAABAAOAAAABAAAAAAABBAMAAAAA":"CgAAAExTS1N0AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBoYWxmNCBjb2xvcjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7CmZsYXQgb3V0IGhhbGY0IHZjb2xvcl9TMDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfMl9TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzEpICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAAAAHMCAAB1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CnVuaWZvcm0gc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MxOwpmbGF0IGluIGhhbGY0IHZjb2xvcl9TMDsKaW4gZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwpoYWxmNCBUZXh0dXJlRWZmZWN0X1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzEsIHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwKS5ycnJyOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMShoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gVGV4dHVyZUVmZmVjdF9TMV9jMChfaW5wdXQpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gTWF0cml4RWZmZWN0X1MxKG91dHB1dENvdmVyYWdlX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAIAAAAcG9zaXRpb24FAAAAY29sb3IAAAAKAAAAbG9jYWxDb29yZAAAAAAAAA==","HUIAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAAAAAADUAANAAAAAAAAAIAAAABLAIABAAAAABAEGABBAMAAAAAAAAAAAAB2AAAAAAACAAAAAEBSAAAAA":"CgAAAExTS1M8AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMDsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzNfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxX2MwKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAC2AgAAdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxX2MwOwp1bmlmb3JtIHNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMTsKaW4gZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc18zX1MwOwpoYWxmNCBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzEsIHZUcmFuc2Zvcm1lZENvb3Jkc18zX1MwKTsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzBfYzAoX2lucHV0KTsKfQpoYWxmNCBCbGVuZF9TMShoYWxmNCBfc3JjLCBoYWxmNCBfZHN0KSAKewoJcmV0dXJuIGJsZW5kX21vZHVsYXRlKE1hdHJpeEVmZmVjdF9TMV9jMChfc3JjKSwgX3NyYyk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzAgPSBoYWxmNCgxKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBCbGVuZF9TMShvdXRwdXRDb2xvcl9TMCwgaGFsZjQoMSkpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAIAAAAcG9zaXRpb24KAAAAbG9jYWxDb29yZAAAAAAAAA==","AYTRVAADQAAAOAEARAFQJAABBADAAAILBYAACCYUQD777777777767YAAAAAAAAAAAAOQAAAAAAAQAAAABAMQAAAAA":"CgAAAExTS1NyAgAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0NCBpbkNpcmNsZUVkZ2U7CmluIGhhbGYzIGluQ2xpcFBsYW5lOwppbiBoYWxmMyBpbklzZWN0UGxhbmU7Cm91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKb3V0IGhhbGYzIHZpbkNsaXBQbGFuZV9TMDsKb3V0IGhhbGYzIHZpbklzZWN0UGxhbmVfUzA7Cm91dCBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgQ2lyY2xlR2VvbWV0cnlQcm9jZXNzb3IKCXZpbkNpcmNsZUVkZ2VfUzAgPSBpbkNpcmNsZUVkZ2U7Cgl2aW5DbGlwUGxhbmVfUzAgPSBpbkNsaXBQbGFuZTsKCXZpbklzZWN0UGxhbmVfUzAgPSBpbklzZWN0UGxhbmU7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8wX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJZmxvYXQyIF90bXBfMV9pblBvc2l0aW9uID0gdWxvY2FsTWF0cml4X1MwLnh6ICogaW5Qb3NpdGlvbiArIHVsb2NhbE1hdHJpeF9TMC55dzsKCXNrX1Bvc2l0aW9uID0gX3RtcF8wX2luUG9zaXRpb24ueHkwMTsKfQoAAAAAAADdAwAAaW4gZmxvYXQ0IHZpbkNpcmNsZUVkZ2VfUzA7CmluIGhhbGYzIHZpbkNsaXBQbGFuZV9TMDsKaW4gaGFsZjMgdmluSXNlY3RQbGFuZV9TMDsKaW4gaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQ0IGNpcmNsZUVkZ2U7CgljaXJjbGVFZGdlID0gdmluQ2lyY2xlRWRnZV9TMDsKCWhhbGYzIGNsaXBQbGFuZTsKCWNsaXBQbGFuZSA9IHZpbkNsaXBQbGFuZV9TMDsKCWhhbGYzIGlzZWN0UGxhbmU7Cglpc2VjdFBsYW5lID0gdmluSXNlY3RQbGFuZV9TMDsKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWZsb2F0IGQgPSBsZW5ndGgoY2lyY2xlRWRnZS54eSk7CgloYWxmIGRpc3RhbmNlVG9PdXRlckVkZ2UgPSBoYWxmKGNpcmNsZUVkZ2UueiAqICgxLjAgLSBkKSk7CgloYWxmIGVkZ2VBbHBoYSA9IHNhdHVyYXRlKGRpc3RhbmNlVG9PdXRlckVkZ2UpOwoJaGFsZiBkaXN0YW5jZVRvSW5uZXJFZGdlID0gaGFsZihjaXJjbGVFZGdlLnogKiAoZCAtIGNpcmNsZUVkZ2UudykpOwoJaGFsZiBpbm5lckFscGhhID0gc2F0dXJhdGUoZGlzdGFuY2VUb0lubmVyRWRnZSk7CgllZGdlQWxwaGEgKj0gaW5uZXJBbHBoYTsKCWhhbGYgY2xpcCA9IGhhbGYoc2F0dXJhdGUoY2lyY2xlRWRnZS56ICogZG90KGNpcmNsZUVkZ2UueHksIGNsaXBQbGFuZS54eSkgKyBjbGlwUGxhbmUueikpOwoJY2xpcCAqPSBoYWxmKHNhdHVyYXRlKGNpcmNsZUVkZ2UueiAqIGRvdChjaXJjbGVFZGdlLnh5LCBpc2VjdFBsYW5lLnh5KSArIGlzZWN0UGxhbmUueikpOwoJZWRnZUFscGhhICo9IGNsaXA7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGVkZ2VBbHBoYSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAoAAABpblBvc2l0aW9uAAAHAAAAaW5Db2xvcgAMAAAAaW5DaXJjbGVFZGdlCwAAAGluQ2xpcFBsYW5lAAwAAABpbklzZWN0UGxhbmUAAAAA","DAQAAAAAAABGAABAYAAQAIHCAIAYAQUBAEAAAAAAEAAAAAAAAAAAAAB2AAAAAAACAAAAAEBSAAAAA":"CgAAAExTS1MWAgAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQyIHVBdGxhc1NpemVJbnZfUzA7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiB1c2hvcnQyIGluVGV4dHVyZUNvb3JkczsKb3V0IGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKZmxhdCBvdXQgZmxvYXQgdlRleEluZGV4X1MwOwpvdXQgaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIEJpdG1hcFRleHQKCWludCB0ZXhJZHggPSAwOwoJZmxvYXQyIHVub3JtVGV4Q29vcmRzID0gZmxvYXQyKGluVGV4dHVyZUNvb3Jkcy54LCBpblRleHR1cmVDb29yZHMueSk7Cgl2VGV4dHVyZUNvb3Jkc19TMCA9IHVub3JtVGV4Q29vcmRzICogdUF0bGFzU2l6ZUludl9TMDsKCXZUZXhJbmRleF9TMCA9IGZsb2F0KHRleElkeCk7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJc2tfUG9zaXRpb24gPSBpblBvc2l0aW9uLnh5MDE7Cn0KAAAAAAAAsQEAAHVuaWZvcm0gc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MwOwppbiBmbG9hdDIgdlRleHR1cmVDb29yZHNfUzA7CmZsYXQgaW4gZmxvYXQgdlRleEluZGV4X1MwOwppbiBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIEJpdG1hcFRleHQKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWhhbGY0IHRleENvbG9yOwoJewoJCXRleENvbG9yID0gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB2VGV4dHVyZUNvb3Jkc19TMCkucnJycjsKCX0KCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gdGV4Q29sb3I7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAoAAABpblBvc2l0aW9uAAAHAAAAaW5Db2xvcgAPAAAAaW5UZXh0dXJlQ29vcmRzAAAAAAA=","HUIAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAAAAAALUAQBAEAQAAAAGQCBAMQACAIAAAAAACQAGAAAAAQAAAAAAAQQGAAAAA":"CgAAAExTS1M2AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAAAAE0DAAB1bmlmb3JtIGZsb2F0NCB1Y2xhbXBfUzFfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKdW5pZm9ybSBzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7CmluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CglmbG9hdDIgaW5Db29yZCA9IHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwoJZmxvYXQyIHN1YnNldENvb3JkOwoJc3Vic2V0Q29vcmQueCA9IGluQ29vcmQueDsKCXN1YnNldENvb3JkLnkgPSBpbkNvb3JkLnk7CglmbG9hdDIgY2xhbXBlZENvb3JkOwoJY2xhbXBlZENvb3JkID0gY2xhbXAoc3Vic2V0Q29vcmQsIHVjbGFtcF9TMV9jMC54eSwgdWNsYW1wX1MxX2MwLnp3KTsKCWhhbGY0IHRleHR1cmVDb2xvciA9IHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMSwgY2xhbXBlZENvb3JkKTsKCXJldHVybiB0ZXh0dXJlQ29sb3I7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBUZXh0dXJlRWZmZWN0X1MxX2MwKF9pbnB1dCk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzAgPSBoYWxmNCgxKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBNYXRyaXhFZmZlY3RfUzEob3V0cHV0Q29sb3JfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACAAAAHBvc2l0aW9uCgAAAGxvY2FsQ29vcmQAAAAAAAA=","HUIAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAAAAAALUAQBAAAQAAAAGQCBAMQACAAAAAAAACQAGAAAAAQAAAAAAAQQGAAAAA":"CgAAAExTS1M2AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAAAAHADAAB1bmlmb3JtIGZsb2F0NCB1Y2xhbXBfUzFfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKdW5pZm9ybSBzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7CmluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CglmbG9hdDIgaW5Db29yZCA9IHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwoJZmxvYXQyIHN1YnNldENvb3JkOwoJc3Vic2V0Q29vcmQueCA9IGluQ29vcmQueDsKCXN1YnNldENvb3JkLnkgPSBpbkNvb3JkLnk7CglmbG9hdDIgY2xhbXBlZENvb3JkOwoJY2xhbXBlZENvb3JkLnggPSBjbGFtcChzdWJzZXRDb29yZC54LCB1Y2xhbXBfUzFfYzAueCwgdWNsYW1wX1MxX2MwLnopOwoJY2xhbXBlZENvb3JkLnkgPSBzdWJzZXRDb29yZC55OwoJaGFsZjQgdGV4dHVyZUNvbG9yID0gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MxLCBjbGFtcGVkQ29vcmQpOwoJcmV0dXJuIHRleHR1cmVDb2xvcjsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzAoX2lucHV0KTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMCA9IGhhbGY0KDEpOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IE1hdHJpeEVmZmVjdF9TMShvdXRwdXRDb2xvcl9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0X1MxICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAIAAAAcG9zaXRpb24KAAAAbG9jYWxDb29yZAAAAAAAAA==","B2AAQAAABQAAIAABBYAAB7777777777774ABICAAAAAAAAAAAAAABUABAAAAAEAAAAAIBEABAAAAA":"CgAAAExTS1MOAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmIGluQ292ZXJhZ2U7Cm91dCBoYWxmIHZpbkNvdmVyYWdlX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBEZWZhdWx0R2VvbWV0cnlQcm9jZXNzb3IKCWZsb2F0MiBfdG1wXzFfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247Cgl2aW5Db3ZlcmFnZV9TMCA9IGluQ292ZXJhZ2U7Cglza19Qb3NpdGlvbiA9IF90bXBfMV9pblBvc2l0aW9uLnh5MDE7Cn0KAAAAAAAATQEAAHVuaWZvcm0gaGFsZjQgdUNvbG9yX1MwOwppbiBoYWxmIHZpbkNvdmVyYWdlX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgRGVmYXVsdEdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdUNvbG9yX1MwOwoJaGFsZiBhbHBoYSA9IDEuMDsKCWFscGhhID0gdmluQ292ZXJhZ2VfUzA7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGFscGhhKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACgAAAGluUG9zaXRpb24AAAoAAABpbkNvdmVyYWdlAAAAAAAA","GEMAAAYAAEHAAAARC4EAAAQWBQAAAAAAAAAQAAAAIBCAAAGQAEAAAAAQAAAABAEQAEAAAAA":"CgAAAExTS1NUAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiBoYWxmMyBpblNoYWRvd1BhcmFtczsKb3V0IGhhbGYzIHZpblNoYWRvd1BhcmFtc19TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBSUmVjdFNoYWRvdwoJdmluU2hhZG93UGFyYW1zX1MwID0gaW5TaGFkb3dQYXJhbXM7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8wX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgAAAAALAgAAdW5pZm9ybSBzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzA7CmluIGhhbGYzIHZpblNoYWRvd1BhcmFtc19TMDsKaW4gaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBSUmVjdFNoYWRvdwoJaGFsZjMgc2hhZG93UGFyYW1zOwoJc2hhZG93UGFyYW1zID0gdmluU2hhZG93UGFyYW1zX1MwOwoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZpbkNvbG9yX1MwOwoJaGFsZiBkID0gbGVuZ3RoKHNoYWRvd1BhcmFtcy54eSk7CglmbG9hdDIgdXYgPSBmbG9hdDIoc2hhZG93UGFyYW1zLnogKiAoMS4wIC0gZCksIDAuNSk7CgloYWxmIGZhY3RvciA9IHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMCwgdXYpLjAwMHIuYTsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoZmFjdG9yKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAoAAABpblBvc2l0aW9uAAAHAAAAaW5Db2xvcgAOAAAAaW5TaGFkb3dQYXJhbXMAAAAAAAA=","DASAAAAAQAAWAABAYAAQBYH7777Z6QQBAEAAAAAAEAAAAAAAEBSAAAB2AAAAAAACAAAAAEBSAAAAA":"CgAAAExTS1PVAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQyIHVBdGxhc1NpemVJbnZfUzA7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiB1c2hvcnQyIGluVGV4dHVyZUNvb3JkczsKb3V0IGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKZmxhdCBvdXQgZmxvYXQgdlRleEluZGV4X1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBCaXRtYXBUZXh0CglpbnQgdGV4SWR4ID0gMDsKCWZsb2F0MiB1bm9ybVRleENvb3JkcyA9IGZsb2F0MihpblRleHR1cmVDb29yZHMueCwgaW5UZXh0dXJlQ29vcmRzLnkpOwoJdlRleHR1cmVDb29yZHNfUzAgPSB1bm9ybVRleENvb3JkcyAqIHVBdGxhc1NpemVJbnZfUzA7Cgl2VGV4SW5kZXhfUzAgPSBmbG9hdCh0ZXhJZHgpOwoJZmxvYXQyIF90bXBfMV9pblBvc2l0aW9uID0gaW5Qb3NpdGlvbjsKCXNrX1Bvc2l0aW9uID0gaW5Qb3NpdGlvbi54eTAxOwp9CgAAAAAAAADgAQAAdW5pZm9ybSBoYWxmNCB1Q29sb3JfUzA7CnVuaWZvcm0gc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MwOwppbiBmbG9hdDIgdlRleHR1cmVDb29yZHNfUzA7CmZsYXQgaW4gZmxvYXQgdlRleEluZGV4X1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgQml0bWFwVGV4dAoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHVDb2xvcl9TMDsKCWhhbGY0IHRleENvbG9yOwoJewoJCXRleENvbG9yID0gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB2VGV4dHVyZUNvb3Jkc19TMCk7Cgl9CglvdXRwdXRDb2xvcl9TMCA9IG91dHB1dENvbG9yX1MwICogdGV4Q29sb3I7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAKAAAAaW5Qb3NpdGlvbgAADwAAAGluVGV4dHVyZUNvb3JkcwAAAAAA","HVIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAFQBQU7BTXIAAAAAAAAAACAAAAAVQEAAQAAAAAQCDAEQQGAAAAAAAAAAAA4IAPAAACAAAAAAAEABYAAAAEAAAAAAAEEBQA":"CgAAAExTS1N6AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMDsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBoYWxmNCBjb2xvcjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7CmZsYXQgb3V0IGhhbGY0IHZjb2xvcl9TMDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfM19TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzFfYzApICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAABAAAArAQAAHVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMDsKdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMjsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzI7CnVuaWZvcm0gc2FtcGxlckV4dGVybmFsT0VTIHVUZXh0dXJlU2FtcGxlcl8wX1MxOwpmbGF0IGluIGhhbGY0IHZjb2xvcl9TMDsKaW4gZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc18zX1MwOwpoYWxmNCBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzEsIHZUcmFuc2Zvcm1lZENvb3Jkc18zX1MwKTsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzBfYzAoX2lucHV0KTsKfQpoYWxmNCBEaXNhYmxlQ292ZXJhZ2VBc0FscGhhX1MxKGhhbGY0IF9pbnB1dCkgCnsKCV9pbnB1dCA9IE1hdHJpeEVmZmVjdF9TMV9jMChfaW5wdXQpOwoJaGFsZjQgX3RtcF8wX2luQ29sb3IgPSBfaW5wdXQ7CglyZXR1cm4gaGFsZjQoX2lucHV0KTsKfQpoYWxmNCBDaXJjdWxhclJSZWN0X1MyKGhhbGY0IF9pbnB1dCkgCnsKCWZsb2F0MiBkeHkwID0gdWlubmVyUmVjdF9TMi5MVCAtIHNrX0ZyYWdDb29yZC54eTsKCWZsb2F0MiBkeHkxID0gc2tfRnJhZ0Nvb3JkLnh5IC0gdWlubmVyUmVjdF9TMi5SQjsKCWZsb2F0MiBkeHkgPSBtYXgobWF4KGR4eTAsIGR4eTEpLCAwLjApOwoJaGFsZiBhbHBoYSA9IGhhbGYoc2F0dXJhdGUodXJhZGl1c1BsdXNIYWxmX1MyLnggLSBsZW5ndGgoZHh5KSkpOwoJcmV0dXJuIF9pbnB1dCAqIGFscGhhOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gRGlzYWJsZUNvdmVyYWdlQXNBbHBoYV9TMShvdXRwdXRDb2xvcl9TMCk7CgloYWxmNCBvdXRwdXRfUzI7CglvdXRwdXRfUzIgPSBDaXJjdWxhclJSZWN0X1MyKG91dHB1dENvdmVyYWdlX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRfUzEgKiBvdXRwdXRfUzI7Cgl9Cn0KAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAIAAAAcG9zaXRpb24FAAAAY29sb3IAAAAKAAAAbG9jYWxDb29yZAAAAAAAAA==","HUIAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAAAAAALUAQMAAAAAAABAEAAAABJYQAAAAAACAIAAAAAWCBAAAIBAAAAANAECAZAAAAQAAAAAAFAAMAAAABAAAAAAABBAM":"CgAAAExTS1M2AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAAAAC8GAAB1bmlmb3JtIGZsb2F0NCB1Y2xhbXBfUzFfYzBfYzBfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMDsKdW5pZm9ybSBoYWxmMiB1SW5jcmVtZW50X1MxX2MwOwp1bmlmb3JtIGhhbGYyIHVPZmZzZXRzQW5kS2VybmVsX1MxX2MwWzEzXTsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxOwp1bmlmb3JtIHNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMTsKaW4gZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwpoYWxmNCBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwX2MwKGhhbGY0IF9pbnB1dCwgZmxvYXQyIF9jb29yZHMpIAp7CglmbG9hdDIgaW5Db29yZCA9IF9jb29yZHM7CglmbG9hdDIgc3Vic2V0Q29vcmQ7CglzdWJzZXRDb29yZC54ID0gaW5Db29yZC54OwoJc3Vic2V0Q29vcmQueSA9IGluQ29vcmQueTsKCWZsb2F0MiBjbGFtcGVkQ29vcmQ7CgljbGFtcGVkQ29vcmQueCA9IHN1YnNldENvb3JkLng7CgljbGFtcGVkQ29vcmQueSA9IGNsYW1wKHN1YnNldENvb3JkLnksIHVjbGFtcF9TMV9jMF9jMF9jMC55LCB1Y2xhbXBfUzFfYzBfYzBfYzAudyk7CgloYWxmNCB0ZXh0dXJlQ29sb3IgPSBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzEsIGNsYW1wZWRDb29yZCk7CglyZXR1cm4gdGV4dHVyZUNvbG9yOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMV9jMF9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBfY29vcmRzKSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzBfYzBfYzAoX2lucHV0LCBmbG9hdDN4Mih1bWF0cml4X1MxX2MwX2MwKSAqIF9jb29yZHMueHkxKTsKfQpoYWxmNCBTbW9vdGhfUzFfYzAoaGFsZjQgX2lucHV0LCBmbG9hdDIgY29vcmQsIGhhbGYyIG9mZnNldEFuZEtlcm5lbCkgCnsKCXJldHVybiBNYXRyaXhFZmZlY3RfUzFfYzBfYzAoX2lucHV0LCAoY29vcmQgKyBvZmZzZXRBbmRLZXJuZWwueCAqIHVJbmNyZW1lbnRfUzFfYzApKSAqIG9mZnNldEFuZEtlcm5lbC55Owp9CmhhbGY0IEdhdXNzaWFuQ29udm9sdXRpb25fUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgY29sb3IgPSBoYWxmNCgwKTsKCWZsb2F0MiBjb29yZCA9IHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwoJZm9yIChpbnQgaT0wOyBpPDEzOyArK2kpIAoJewoJCWNvbG9yICs9IFNtb290aF9TMV9jMChfaW5wdXQsIGNvb3JkLCB1T2Zmc2V0c0FuZEtlcm5lbF9TMV9jMFtpXSk7Cgl9CglyZXR1cm4gY29sb3I7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBHYXVzc2lhbkNvbnZvbHV0aW9uX1MxX2MwKF9pbnB1dCk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzAgPSBoYWxmNCgxKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBNYXRyaXhFZmZlY3RfUzEob3V0cHV0Q29sb3JfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgoAAABsb2NhbENvb3JkAAAAAAAA","B2ABSAAABQAAIAABBYAAB7777777777774ABICAAAAAAAAAAAAAABUABAAAAAEAAAAAIBEABAAAAA":"CgAAAExTS1N4AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gaGFsZjQgdUNvbG9yX1MwOwppbiBmbG9hdDIgaW5Qb3NpdGlvbjsKaW4gaGFsZiBpbkNvdmVyYWdlOwpvdXQgaGFsZjQgdmNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBEZWZhdWx0R2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IGNvbG9yID0gdUNvbG9yX1MwOwoJY29sb3IgPSBjb2xvciAqIGluQ292ZXJhZ2U7Cgl2Y29sb3JfUzAgPSBjb2xvcjsKCWZsb2F0MiBfdG1wXzFfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8zX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJc2tfUG9zaXRpb24gPSBfdG1wXzFfaW5Qb3NpdGlvbi54eTAxOwp9CgAAAAAGAQAAaW4gaGFsZjQgdmNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgRGVmYXVsdEdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAKAAAAaW5Qb3NpdGlvbgAACgAAAGluQ292ZXJhZ2UAAAAAAAA=","HUJAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAQAAAAAAQQGACQAGAAAAAQAAAAAAAQQGAAA":"CgAAAExTS1PlAAAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7Cm91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmxvY2FsQ29vcmRfUzAgPSBsb2NhbENvb3JkOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAAAAAACsAQAAdW5pZm9ybSBzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzA7CmluIGZsb2F0MiB2bG9jYWxDb29yZF9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzAgPSBoYWxmNCgxKTsKCWZsb2F0MiB0ZXhDb29yZDsKCXRleENvb3JkID0gdmxvY2FsQ29vcmRfUzA7CglvdXRwdXRDb2xvcl9TMCA9IChibGVuZF9tb2R1bGF0ZShzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzAsIHRleENvb3JkKSwgaGFsZjQoMSkpKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgoAAABsb2NhbENvb3JkAAAAAAAA","AYQA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAACABZQA6AAAEAAAAAAAIADQAAAAIAAAAAAAIIDA":"CgAAAExTS1PMAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0NCBpbkNpcmNsZUVkZ2U7Cm91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJdmluQ2lyY2xlRWRnZV9TMCA9IGluQ2lyY2xlRWRnZTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSB1bG9jYWxNYXRyaXhfUzAueHogKiBpblBvc2l0aW9uICsgdWxvY2FsTWF0cml4X1MwLnl3OwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgEAAACPAwAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CmluIGZsb2F0NCB2aW5DaXJjbGVFZGdlX1MwOwppbiBoYWxmNCB2aW5Db2xvcl9TMDsKaGFsZjQgQ2lyY3VsYXJSUmVjdF9TMShoYWxmNCBfaW5wdXQpIAp7CglmbG9hdDIgZHh5MCA9IHVpbm5lclJlY3RfUzEuTFQgLSBza19GcmFnQ29vcmQueHk7CglmbG9hdDIgZHh5MSA9IHNrX0ZyYWdDb29yZC54eSAtIHVpbm5lclJlY3RfUzEuUkI7CglmbG9hdDIgZHh5ID0gbWF4KG1heChkeHkwLCBkeHkxKSwgMC4wKTsKCWhhbGYgYWxwaGEgPSBoYWxmKHNhdHVyYXRlKHVyYWRpdXNQbHVzSGFsZl9TMS54IC0gbGVuZ3RoKGR4eSkpKTsKCWFscGhhID0gMS4wIC0gYWxwaGE7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIENpcmNsZUdlb21ldHJ5UHJvY2Vzc29yCglmbG9hdDQgY2lyY2xlRWRnZTsKCWNpcmNsZUVkZ2UgPSB2aW5DaXJjbGVFZGdlX1MwOwoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZpbkNvbG9yX1MwOwoJZmxvYXQgZCA9IGxlbmd0aChjaXJjbGVFZGdlLnh5KTsKCWhhbGYgZGlzdGFuY2VUb091dGVyRWRnZSA9IGhhbGYoY2lyY2xlRWRnZS56ICogKDEuMCAtIGQpKTsKCWhhbGYgZWRnZUFscGhhID0gc2F0dXJhdGUoZGlzdGFuY2VUb091dGVyRWRnZSk7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGVkZ2VBbHBoYSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBDaXJjdWxhclJSZWN0X1MxKG91dHB1dENvdmVyYWdlX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADAAAAGluQ2lyY2xlRWRnZQAAAAA=","CMRQCIAABBYAAAEIXBAAACDQMAABRAFAAAAAAAAAAAAAAAEABYAAAAEAAAAAAAEEBQAAAAA":"CgAAAExTS1MyAgAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0MiBpbkVsbGlwc2VPZmZzZXQ7CmluIGZsb2F0NCBpbkVsbGlwc2VSYWRpaTsKb3V0IGZsb2F0MiB2RWxsaXBzZU9mZnNldHNfUzA7Cm91dCBmbG9hdDQgdkVsbGlwc2VSYWRpaV9TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBFbGxpcHNlR2VvbWV0cnlQcm9jZXNzb3IKCXZFbGxpcHNlT2Zmc2V0c19TMCA9IGluRWxsaXBzZU9mZnNldDsKCXZFbGxpcHNlUmFkaWlfUzAgPSBpbkVsbGlwc2VSYWRpaTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSB1bG9jYWxNYXRyaXhfUzAueHogKiBpblBvc2l0aW9uICsgdWxvY2FsTWF0cml4X1MwLnl3OwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgAAAAAAAHIDAABpbiBmbG9hdDIgdkVsbGlwc2VPZmZzZXRzX1MwOwppbiBmbG9hdDQgdkVsbGlwc2VSYWRpaV9TMDsKaW4gaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBFbGxpcHNlR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWZsb2F0MiBvZmZzZXQgPSB2RWxsaXBzZU9mZnNldHNfUzAueHk7CglvZmZzZXQgKj0gdkVsbGlwc2VSYWRpaV9TMC54eTsKCWZsb2F0IHRlc3QgPSBkb3Qob2Zmc2V0LCBvZmZzZXQpIC0gMS4wOwoJZmxvYXQyIGdyYWQgPSAyLjAqb2Zmc2V0KnZFbGxpcHNlUmFkaWlfUzAueHk7CglmbG9hdCBncmFkX2RvdCA9IGRvdChncmFkLCBncmFkKTsKCWdyYWRfZG90ID0gbWF4KGdyYWRfZG90LCAxLjE3NTVlLTM4KTsKCWZsb2F0IGludmxlbiA9IGludmVyc2VzcXJ0KGdyYWRfZG90KTsKCWZsb2F0IGVkZ2VBbHBoYSA9IHNhdHVyYXRlKDAuNS10ZXN0Kmludmxlbik7CglvZmZzZXQgPSB2RWxsaXBzZU9mZnNldHNfUzAueHkqdkVsbGlwc2VSYWRpaV9TMC56dzsKCXRlc3QgPSBkb3Qob2Zmc2V0LCBvZmZzZXQpIC0gMS4wOwoJZ3JhZCA9IDIuMCpvZmZzZXQqdkVsbGlwc2VSYWRpaV9TMC56dzsKCWdyYWRfZG90ID0gZG90KGdyYWQsIGdyYWQpOwoJaW52bGVuID0gaW52ZXJzZXNxcnQoZ3JhZF9kb3QpOwoJZWRnZUFscGhhICo9IHNhdHVyYXRlKDAuNSt0ZXN0Kmludmxlbik7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGhhbGYoZWRnZUFscGhhKSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACgAAAGluUG9zaXRpb24AAAcAAABpbkNvbG9yAA8AAABpbkVsbGlwc2VPZmZzZXQADgAAAGluRWxsaXBzZVJhZGlpAAAAAAAA","HUQACAAAAAMAADAAAIOAAAH677776IZOCAAP577777777777777777YBAAAAAAAAAAAKAAYAAAACAAAAAAACCAYAAA":"CgAAAExTS1PPAAAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7Cm91dCBoYWxmNCB2Y29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cn0KAAAAAAAMAQAAaW4gaGFsZjQgdmNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAAAAAAAA==","HUIAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAAAAAALUAQAAEAQAAAAGQCBAMQAAAIAAAAAACQAGAAAAAQAAAAAAAQQGAAAAA":"CgAAAExTS1M2AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAAAAHADAAB1bmlmb3JtIGZsb2F0NCB1Y2xhbXBfUzFfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKdW5pZm9ybSBzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7CmluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CglmbG9hdDIgaW5Db29yZCA9IHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwoJZmxvYXQyIHN1YnNldENvb3JkOwoJc3Vic2V0Q29vcmQueCA9IGluQ29vcmQueDsKCXN1YnNldENvb3JkLnkgPSBpbkNvb3JkLnk7CglmbG9hdDIgY2xhbXBlZENvb3JkOwoJY2xhbXBlZENvb3JkLnggPSBzdWJzZXRDb29yZC54OwoJY2xhbXBlZENvb3JkLnkgPSBjbGFtcChzdWJzZXRDb29yZC55LCB1Y2xhbXBfUzFfYzAueSwgdWNsYW1wX1MxX2MwLncpOwoJaGFsZjQgdGV4dHVyZUNvbG9yID0gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MxLCBjbGFtcGVkQ29vcmQpOwoJcmV0dXJuIHRleHR1cmVDb2xvcjsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzAoX2lucHV0KTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMCA9IGhhbGY0KDEpOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IE1hdHJpeEVmZmVjdF9TMShvdXRwdXRDb2xvcl9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0X1MxICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAIAAAAcG9zaXRpb24KAAAAbG9jYWxDb29yZAAAAAAAAA==","FAAQMYAAMAAAEADAAABAEYAAAICIAB5AABQAAAQAMAAAEATAAABAIIGAAEDCBYQCA4AAAAAAEAKPABAAAAAAB2AAAAAAACAAAAAEBSAAAAAAA":"CgAAAExTS1PUCwAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0NCByYWRpaV9zZWxlY3RvcjsKaW4gZmxvYXQ0IGNvcm5lcl9hbmRfcmFkaXVzX291dHNldHM7CmluIGZsb2F0NCBhYV9ibG9hdF9hbmRfY292ZXJhZ2U7CmluIGZsb2F0NCByYWRpaV94OwppbiBmbG9hdDQgcmFkaWlfeTsKaW4gZmxvYXQ0IHNrZXc7CmluIGZsb2F0MiB0cmFuc2xhdGVfYW5kX2xvY2Fscm90YXRlOwppbiBoYWxmNCBjb2xvcjsKZmxhdCBvdXQgaGFsZjQgdmNvbG9yX1MwOwpvdXQgZmxvYXQyIHZhcmNjb29yZF9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgRmlsbFJSZWN0T3A6OlByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7CglmbG9hdCBhYV9ibG9hdF9tdWx0aXBsaWVyID0gMTsKCWZsb2F0MiBjb3JuZXIgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnh5OwoJZmxvYXQyIHJhZGl1c19vdXRzZXQgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnp3OwoJZmxvYXQyIGFhX2Jsb2F0X2RpcmVjdGlvbiA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS54eTsKCWZsb2F0IGlzX2xpbmVhcl9jb3ZlcmFnZSA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS53OwoJZmxvYXQyIHBpeGVsbGVuZ3RoID0gaW52ZXJzZXNxcnQoZmxvYXQyKGRvdChza2V3Lnh6LCBza2V3Lnh6KSwgZG90KHNrZXcueXcsIHNrZXcueXcpKSk7CglmbG9hdDQgbm9ybWFsaXplZF9heGlzX2RpcnMgPSBza2V3ICogcGl4ZWxsZW5ndGgueHl4eTsKCWZsb2F0MiBheGlzd2lkdGhzID0gKGFicyhub3JtYWxpemVkX2F4aXNfZGlycy54eSkgKyBhYnMobm9ybWFsaXplZF9heGlzX2RpcnMuencpKTsKCWZsb2F0MiBhYV9ibG9hdHJhZGl1cyA9IGF4aXN3aWR0aHMgKiBwaXhlbGxlbmd0aCAqIC41OwoJZmxvYXQ0IHJhZGlpX2FuZF9uZWlnaGJvcnMgPSByYWRpaV9zZWxlY3RvciogZmxvYXQ0eDQocmFkaWlfeCwgcmFkaWlfeSwgcmFkaWlfeC55eHd6LCByYWRpaV95Lnd6eXgpOwoJZmxvYXQyIHJhZGlpID0gcmFkaWlfYW5kX25laWdoYm9ycy54eTsKCWZsb2F0MiBuZWlnaGJvcl9yYWRpaSA9IHJhZGlpX2FuZF9uZWlnaGJvcnMuenc7CglmbG9hdCBjb3ZlcmFnZV9tdWx0aXBsaWVyID0gMTsKCWlmIChhbnkoZ3JlYXRlclRoYW4oYWFfYmxvYXRyYWRpdXMsIGZsb2F0MigxKSkpKSAKCXsKCQljb3JuZXIgPSBtYXgoYWJzKGNvcm5lciksIGFhX2Jsb2F0cmFkaXVzKSAqIHNpZ24oY29ybmVyKTsKCQljb3ZlcmFnZV9tdWx0aXBsaWVyID0gMSAvIChtYXgoYWFfYmxvYXRyYWRpdXMueCwgMSkgKiBtYXgoYWFfYmxvYXRyYWRpdXMueSwgMSkpOwoJCXJhZGlpID0gZmxvYXQyKDApOwoJfQoJZmxvYXQgY292ZXJhZ2UgPSBhYV9ibG9hdF9hbmRfY292ZXJhZ2UuejsKCWlmIChhbnkobGVzc1RoYW4ocmFkaWksIGFhX2Jsb2F0cmFkaXVzICogMS41KSkpIAoJewoJCXJhZGlpID0gZmxvYXQyKDApOwoJCWFhX2Jsb2F0X2RpcmVjdGlvbiA9IHNpZ24oY29ybmVyKTsKCQlpZiAoY292ZXJhZ2UgPiAuNSkgCgkJewoJCQlhYV9ibG9hdF9kaXJlY3Rpb24gPSAtYWFfYmxvYXRfZGlyZWN0aW9uOwoJCX0KCQlpc19saW5lYXJfY292ZXJhZ2UgPSAxOwoJfQoJZWxzZSAKCXsKCQlyYWRpaSA9IGNsYW1wKHJhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQluZWlnaGJvcl9yYWRpaSA9IGNsYW1wKG5laWdoYm9yX3JhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQlmbG9hdDIgc3BhY2luZyA9IDIgLSByYWRpaSAtIG5laWdoYm9yX3JhZGlpOwoJCWZsb2F0MiBleHRyYV9wYWQgPSBtYXgocGl4ZWxsZW5ndGggKiAuMDYyNSAtIHNwYWNpbmcsIGZsb2F0MigwKSk7CgkJcmFkaWkgLT0gZXh0cmFfcGFkICogLjU7Cgl9CglmbG9hdDIgYWFfb3V0c2V0ID0gYWFfYmxvYXRfZGlyZWN0aW9uICogYWFfYmxvYXRyYWRpdXMgKiBhYV9ibG9hdF9tdWx0aXBsaWVyOwoJZmxvYXQyIHZlcnRleHBvcyA9IGNvcm5lciArIHJhZGl1c19vdXRzZXQgKiByYWRpaSArIGFhX291dHNldDsKCWlmIChjb3ZlcmFnZSA+IC41KSAKCXsKCQlpZiAoYWFfYmxvYXRfZGlyZWN0aW9uLnggIT0gMCAmJiB2ZXJ0ZXhwb3MueCAqIGNvcm5lci54IDwgMCkgCgkJewoJCQlmbG9hdCBiYWNrc2V0ID0gYWJzKHZlcnRleHBvcy54KTsKCQkJdmVydGV4cG9zLnggPSAwOwoJCQl2ZXJ0ZXhwb3MueSArPSBiYWNrc2V0ICogc2lnbihjb3JuZXIueSkgKiBwaXhlbGxlbmd0aC55L3BpeGVsbGVuZ3RoLng7CgkJCWNvdmVyYWdlID0gKGNvdmVyYWdlIC0gLjUpICogYWJzKGNvcm5lci54KSAvIChhYnMoY29ybmVyLngpICsgYmFja3NldCkgKyAuNTsKCQl9CgkJaWYgKGFhX2Jsb2F0X2RpcmVjdGlvbi55ICE9IDAgJiYgdmVydGV4cG9zLnkgKiBjb3JuZXIueSA8IDApIAoJCXsKCQkJZmxvYXQgYmFja3NldCA9IGFicyh2ZXJ0ZXhwb3MueSk7CgkJCXZlcnRleHBvcy55ID0gMDsKCQkJdmVydGV4cG9zLnggKz0gYmFja3NldCAqIHNpZ24oY29ybmVyLngpICogcGl4ZWxsZW5ndGgueC9waXhlbGxlbmd0aC55OwoJCQljb3ZlcmFnZSA9IChjb3ZlcmFnZSAtIC41KSAqIGFicyhjb3JuZXIueSkgLyAoYWJzKGNvcm5lci55KSArIGJhY2tzZXQpICsgLjU7CgkJfQoJfQoJZmxvYXQyeDIgc2tld21hdHJpeCA9IGZsb2F0MngyKHNrZXcueHksIHNrZXcuencpOwoJZmxvYXQyIGRldmNvb3JkID0gdmVydGV4cG9zICogc2tld21hdHJpeCArIHRyYW5zbGF0ZV9hbmRfbG9jYWxyb3RhdGUueHk7CglpZiAoMCAhPSBpc19saW5lYXJfY292ZXJhZ2UpIAoJewoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MigwLCBjb3ZlcmFnZSAqIGNvdmVyYWdlX211bHRpcGxpZXIpOwoJfQoJZWxzZSAKCXsKCQlmbG9hdDIgYXJjY29vcmQgPSAxIC0gYWJzKHJhZGl1c19vdXRzZXQpICsgYWFfb3V0c2V0L3JhZGlpICogY29ybmVyOwoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MihhcmNjb29yZC54KzEsIGFyY2Nvb3JkLnkpOwoJfQoJc2tfUG9zaXRpb24gPSBkZXZjb29yZC54eTAxOwp9CgEAAACbBAAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBmbG9hdDIgdWludlJhZGlpWFlfUzE7CmZsYXQgaW4gaGFsZjQgdmNvbG9yX1MwOwppbiBmbG9hdDIgdmFyY2Nvb3JkX1MwOwpoYWxmNCBFbGxpcHRpY2FsUlJlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MxLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MxLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CglmbG9hdDIgWiA9IGR4eSAqIHVpbnZSYWRpaVhZX1MxLnh5OwoJaGFsZiBpbXBsaWNpdCA9IGhhbGYoZG90KFosIGR4eSkgLSAxLjApOwoJaGFsZiBncmFkX2RvdCA9IGhhbGYoNC4wICogZG90KFosIFopKTsKCWdyYWRfZG90ID0gbWF4KGdyYWRfZG90LCAxLjBlLTQpOwoJaGFsZiBhcHByb3hfZGlzdCA9IGltcGxpY2l0ICogaGFsZihpbnZlcnNlc3FydChncmFkX2RvdCkpOwoJaGFsZiBhbHBoYSA9IGNsYW1wKDAuNSArIGFwcHJveF9kaXN0LCAwLjAsIDEuMCk7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIEZpbGxSUmVjdE9wOjpQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7CglmbG9hdCB4X3BsdXNfMT12YXJjY29vcmRfUzAueCwgeT12YXJjY29vcmRfUzAueTsKCWhhbGYgY292ZXJhZ2U7CglpZiAoMCA9PSB4X3BsdXNfMSkgCgl7CgkJY292ZXJhZ2UgPSBoYWxmKHkpOwoJfQoJZWxzZSAKCXsKCQlmbG9hdCBmbiA9IHhfcGx1c18xICogKHhfcGx1c18xIC0gMik7CgkJZm4gPSBmbWEoeSx5LCBmbik7CgkJZmxvYXQgZm53aWR0aCA9IGZ3aWR0aChmbik7CgkJY292ZXJhZ2UgPSAuNSAtIGhhbGYoZm4vZm53aWR0aCk7CgkJY292ZXJhZ2UgPSBjbGFtcChjb3ZlcmFnZSwgMCwgMSk7Cgl9CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGNvdmVyYWdlKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IEVsbGlwdGljYWxSUmVjdF9TMShvdXRwdXRDb3ZlcmFnZV9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzE7Cgl9Cn0KAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAIAAAADgAAAHJhZGlpX3NlbGVjdG9yAAAZAAAAY29ybmVyX2FuZF9yYWRpdXNfb3V0c2V0cwAAABUAAABhYV9ibG9hdF9hbmRfY292ZXJhZ2UAAAAHAAAAcmFkaWlfeAAHAAAAcmFkaWlfeQAEAAAAc2tldxkAAAB0cmFuc2xhdGVfYW5kX2xvY2Fscm90YXRlAAAABQAAAGNvbG9yAAAAAAAAAA==","HVIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAFIBQU7BTXIAAAAAACAWXW3ZEQAAAADAAAAAGATHIBICYCAAAEBP2LBIPAAAAAIAAAAAEARRALJ3F5SMAAAABQAAAABABTUEURMBAAACAH5FYUHQAAAAAAAEAAAAAZ4RGGRCQFAEAAAAAAAAAGARP2LVJPAAAAAAAAEAAAABSKRXZFAUHQAAAAAAAAAACAA4AAAACAAAAAAACCAYAA":"CgAAAExTS1OAAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBoYWxmNCBjb2xvcjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7CmZsYXQgb3V0IGhhbGY0IHZjb2xvcl9TMDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfNV9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfNV9TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzFfYzBfYzEpICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAAAAIIGAAB1bmlmb3JtIGhhbGY0IHVzdGFydF9TMV9jMF9jMDsKdW5pZm9ybSBoYWxmNCB1ZW5kX1MxX2MwX2MwOwp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzBfYzE7CnVuaWZvcm0gaGFsZjQgdWxlZnRCb3JkZXJDb2xvcl9TMV9jMDsKdW5pZm9ybSBoYWxmNCB1cmlnaHRCb3JkZXJDb2xvcl9TMV9jMDsKZmxhdCBpbiBoYWxmNCB2Y29sb3JfUzA7CmluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfNV9TMDsKaGFsZjQgU2luZ2xlSW50ZXJ2YWxDb2xvcml6ZXJfUzFfYzBfYzAoaGFsZjQgX2lucHV0LCBmbG9hdDIgX2Nvb3JkcykgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJZmxvYXQyIF90bXBfMV9jb29yZHMgPSBfY29vcmRzOwoJcmV0dXJuIGhhbGY0KG1peCh1c3RhcnRfUzFfYzBfYzAsIHVlbmRfUzFfYzBfYzAsIGhhbGYoX3RtcF8xX2Nvb3Jkcy54KSkpOwp9CmhhbGY0IExpbmVhckxheW91dF9TMV9jMF9jMV9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzJfaW5Db2xvciA9IF9pbnB1dDsKCWZsb2F0MiBfdG1wXzNfY29vcmRzID0gdlRyYW5zZm9ybWVkQ29vcmRzXzVfUzA7CglyZXR1cm4gaGFsZjQoaGFsZjQoaGFsZihfdG1wXzNfY29vcmRzLngpICsgMWUtMDUsIDEuMCwgMC4wLCAwLjApKTsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzFfYzBfYzEoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIExpbmVhckxheW91dF9TMV9jMF9jMV9jMChfaW5wdXQpOwp9CmhhbGY0IENsYW1wZWRHcmFkaWVudF9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzRfaW5Db2xvciA9IF9pbnB1dDsKCWhhbGY0IHQgPSBNYXRyaXhFZmZlY3RfUzFfYzBfYzEoX3RtcF80X2luQ29sb3IpOwoJaGFsZjQgb3V0Q29sb3I7CglpZiAoIWJvb2woaW50KDEpKSAmJiB0LnkgPCAwLjApIAoJewoJCW91dENvbG9yID0gaGFsZjQoMC4wKTsKCX0KCWVsc2UgaWYgKHQueCA8IDAuMCkgCgl7CgkJb3V0Q29sb3IgPSB1bGVmdEJvcmRlckNvbG9yX1MxX2MwOwoJfQoJZWxzZSBpZiAodC54ID4gMS4wKSAKCXsKCQlvdXRDb2xvciA9IHVyaWdodEJvcmRlckNvbG9yX1MxX2MwOwoJfQoJZWxzZSAKCXsKCQlvdXRDb2xvciA9IFNpbmdsZUludGVydmFsQ29sb3JpemVyX1MxX2MwX2MwKF90bXBfNF9pbkNvbG9yLCBmbG9hdDIoaGFsZjIodC54LCAwLjApKSk7Cgl9CglyZXR1cm4gaGFsZjQob3V0Q29sb3IpOwp9CmhhbGY0IERpc2FibGVDb3ZlcmFnZUFzQWxwaGFfUzEoaGFsZjQgX2lucHV0KSAKewoJX2lucHV0ID0gQ2xhbXBlZEdyYWRpZW50X1MxX2MwKF9pbnB1dCk7CgloYWxmNCBfdG1wXzVfaW5Db2xvciA9IF9pbnB1dDsKCXJldHVybiBoYWxmNChfaW5wdXQpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gRGlzYWJsZUNvdmVyYWdlQXNBbHBoYV9TMShvdXRwdXRDb2xvcl9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0X1MxICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAgAAABwb3NpdGlvbgUAAABjb2xvcgAAAAoAAABsb2NhbENvb3JkAAAAAAAA","AYAA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAAAAOQAAAAAAAQAAAABAMQAAAAA":"CgAAAExTS1OCAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiBmbG9hdDQgaW5DaXJjbGVFZGdlOwpvdXQgZmxvYXQ0IHZpbkNpcmNsZUVkZ2VfUzA7Cm91dCBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgQ2lyY2xlR2VvbWV0cnlQcm9jZXNzb3IKCXZpbkNpcmNsZUVkZ2VfUzAgPSBpbkNpcmNsZUVkZ2U7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8wX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJZmxvYXQyIF90bXBfMl9pblBvc2l0aW9uID0gaW5Qb3NpdGlvbjsKCXNrX1Bvc2l0aW9uID0gX3RtcF8wX2luUG9zaXRpb24ueHkwMTsKfQoAAAAAAADqAQAAaW4gZmxvYXQ0IHZpbkNpcmNsZUVkZ2VfUzA7CmluIGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgQ2lyY2xlR2VvbWV0cnlQcm9jZXNzb3IKCWZsb2F0NCBjaXJjbGVFZGdlOwoJY2lyY2xlRWRnZSA9IHZpbkNpcmNsZUVkZ2VfUzA7CgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmluQ29sb3JfUzA7CglmbG9hdCBkID0gbGVuZ3RoKGNpcmNsZUVkZ2UueHkpOwoJaGFsZiBkaXN0YW5jZVRvT3V0ZXJFZGdlID0gaGFsZihjaXJjbGVFZGdlLnogKiAoMS4wIC0gZCkpOwoJaGFsZiBlZGdlQWxwaGEgPSBzYXR1cmF0ZShkaXN0YW5jZVRvT3V0ZXJFZGdlKTsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoZWRnZUFscGhhKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADAAAAGluQ2lyY2xlRWRnZQAAAAA=","AYQA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAAAAB3QA6AAAEAAAAAAAMAAPEAEAAABAAAAAAB2AAAAAAACAAAAAEBSAAAA":"CgAAAExTS1PMAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0NCBpbkNpcmNsZUVkZ2U7Cm91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJdmluQ2lyY2xlRWRnZV9TMCA9IGluQ2lyY2xlRWRnZTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSB1bG9jYWxNYXRyaXhfUzAueHogKiBpblBvc2l0aW9uICsgdWxvY2FsTWF0cml4X1MwLnl3OwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgEAAAAeBQAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CnVuaWZvcm0gZmxvYXQ0IHVpbm5lclJlY3RfUzI7CnVuaWZvcm0gaGFsZjIgdXJhZGl1c1BsdXNIYWxmX1MyOwppbiBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKaW4gaGFsZjQgdmluQ29sb3JfUzA7CmhhbGY0IENpcmN1bGFyUlJlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MxLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MxLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CgloYWxmIGFscGhhID0gaGFsZihzYXR1cmF0ZSh1cmFkaXVzUGx1c0hhbGZfUzEueCAtIGxlbmd0aChkeHkpKSk7CglhbHBoYSA9IDEuMCAtIGFscGhhOwoJcmV0dXJuIF9pbnB1dCAqIGFscGhhOwp9CmhhbGY0IENpcmN1bGFyUlJlY3RfUzIoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MyLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MyLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CgloYWxmIGFscGhhID0gaGFsZihzYXR1cmF0ZSh1cmFkaXVzUGx1c0hhbGZfUzIueCAtIGxlbmd0aChkeHkpKSk7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIENpcmNsZUdlb21ldHJ5UHJvY2Vzc29yCglmbG9hdDQgY2lyY2xlRWRnZTsKCWNpcmNsZUVkZ2UgPSB2aW5DaXJjbGVFZGdlX1MwOwoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZpbkNvbG9yX1MwOwoJZmxvYXQgZCA9IGxlbmd0aChjaXJjbGVFZGdlLnh5KTsKCWhhbGYgZGlzdGFuY2VUb091dGVyRWRnZSA9IGhhbGYoY2lyY2xlRWRnZS56ICogKDEuMCAtIGQpKTsKCWhhbGYgZWRnZUFscGhhID0gc2F0dXJhdGUoZGlzdGFuY2VUb091dGVyRWRnZSk7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGVkZ2VBbHBoYSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBDaXJjdWxhclJSZWN0X1MxKG91dHB1dENvdmVyYWdlX1MwKTsKCWhhbGY0IG91dHB1dF9TMjsKCW91dHB1dF9TMiA9IENpcmN1bGFyUlJlY3RfUzIob3V0cHV0X1MxKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMjsKCX0KfQoAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACgAAAGluUG9zaXRpb24AAAcAAABpbkNvbG9yAAwAAABpbkNpcmNsZUVkZ2UAAAAA","AYQA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAACABYQA6AAAEAAAAAAAIADQAAAAIAAAAAAAIIDA":"CgAAAExTS1PMAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0NCBpbkNpcmNsZUVkZ2U7Cm91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJdmluQ2lyY2xlRWRnZV9TMCA9IGluQ2lyY2xlRWRnZTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSB1bG9jYWxNYXRyaXhfUzAueHogKiBpblBvc2l0aW9uICsgdWxvY2FsTWF0cml4X1MwLnl3OwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgEAAAB5AwAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CmluIGZsb2F0NCB2aW5DaXJjbGVFZGdlX1MwOwppbiBoYWxmNCB2aW5Db2xvcl9TMDsKaGFsZjQgQ2lyY3VsYXJSUmVjdF9TMShoYWxmNCBfaW5wdXQpIAp7CglmbG9hdDIgZHh5MCA9IHVpbm5lclJlY3RfUzEuTFQgLSBza19GcmFnQ29vcmQueHk7CglmbG9hdDIgZHh5MSA9IHNrX0ZyYWdDb29yZC54eSAtIHVpbm5lclJlY3RfUzEuUkI7CglmbG9hdDIgZHh5ID0gbWF4KG1heChkeHkwLCBkeHkxKSwgMC4wKTsKCWhhbGYgYWxwaGEgPSBoYWxmKHNhdHVyYXRlKHVyYWRpdXNQbHVzSGFsZl9TMS54IC0gbGVuZ3RoKGR4eSkpKTsKCXJldHVybiBfaW5wdXQgKiBhbHBoYTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgQ2lyY2xlR2VvbWV0cnlQcm9jZXNzb3IKCWZsb2F0NCBjaXJjbGVFZGdlOwoJY2lyY2xlRWRnZSA9IHZpbkNpcmNsZUVkZ2VfUzA7CgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmluQ29sb3JfUzA7CglmbG9hdCBkID0gbGVuZ3RoKGNpcmNsZUVkZ2UueHkpOwoJaGFsZiBkaXN0YW5jZVRvT3V0ZXJFZGdlID0gaGFsZihjaXJjbGVFZGdlLnogKiAoMS4wIC0gZCkpOwoJaGFsZiBlZGdlQWxwaGEgPSBzYXR1cmF0ZShkaXN0YW5jZVRvT3V0ZXJFZGdlKTsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoZWRnZUFscGhhKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IENpcmN1bGFyUlJlY3RfUzEob3V0cHV0Q292ZXJhZ2VfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0X1MxOwoJfQp9CgAAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACgAAAGluUG9zaXRpb24AAAcAAABpbkNvbG9yAAwAAABpbkNpcmNsZUVkZ2UAAAAA"}} \ No newline at end of file