From 55126217ae872ee3e0ef914e6db55f8581c0f406 Mon Sep 17 00:00:00 2001 From: Peter Siegmund Date: Thu, 1 Jul 2021 09:42:44 +0200 Subject: [PATCH] updated to null-safety --- .github/workflows/testing.yml | 2 +- CHANGELOG.md | 4 +++ analysis_options.yaml | 1 + example/main.dart | 68 +++++++++++++++++++---------------- lib/src/binary_reader.dart | 2 +- lib/src/latlng.dart | 9 +++++ lib/src/models/cartridge.dart | 67 +++++++++++++++++++++------------- lib/src/models/media.dart | 13 ++++--- lib/src/parser.dart | 4 +-- pubspec.yaml | 4 +-- test/gwc_test.dart | 21 ++++++----- test/latlng_test.dart | 2 +- 12 files changed, 120 insertions(+), 77 deletions(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index a02db2a..5d4da12 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -35,7 +35,7 @@ jobs: steps: - uses: actions/checkout@v2 - - uses: axel-op/dart-package-analyzer/with-full-sdk@stable + - uses: axel-op/dart-package-analyzer@v3 id: analysis # set an id for the current step with: githubToken: ${{ secrets.GITHUB_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md index feee21c..a0c601f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.0 + +- package is now null-safety + ## 0.1.2+2 - remove dart:io - more web compatibility diff --git a/analysis_options.yaml b/analysis_options.yaml index ab5a2b6..a061c97 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -3,6 +3,7 @@ include: package:effective_dart/analysis_options.yaml linter: rules: lines_longer_than_80_chars: false + avoid_equals_and_hash_code_on_mutable_classes: false analyzer: exclude: diff --git a/example/main.dart b/example/main.dart index cf758be..182033d 100644 --- a/example/main.dart +++ b/example/main.dart @@ -3,10 +3,10 @@ import 'dart:io'; import 'package:freewig/freewig.dart'; -Future parseFile(File file) async { +Future parseFile(File file) async { final bytes = await file.readAsBytes(); final completer = Completer(); - await Future(() => parseData(bytes)) + await Future(() => parseData(bytes)) .then(completer.complete) .catchError(completer.completeError); return completer.future; @@ -16,34 +16,42 @@ void main(List arguments) async { var file = File(arguments[0]); // path/incl/cartridge.gwc var cartridge = await parseFile(file); - var export = Directory('export'); - var exists = await export.exists(); - if (exists) { - await export.delete(recursive: true); - } - await export.create(recursive: true); + if (cartridge != null) { + var export = Directory('export'); + var exists = await export.exists(); + if (exists) { + await export.delete(recursive: true); + } + await export.create(recursive: true); - var contents = ''; - contents += 'Cartridge-Id: ${cartridge.cartridgeGuid}\n'; - contents += 'Name: ${cartridge.cartridgeName}\n'; - contents += 'Description: ${cartridge.cartridgeDesc}\n'; - contents += 'StartLocation: ${cartridge.startLocationDesc}\n'; - contents += 'Latitude: ${cartridge.latLng.latitude}\n'; - contents += 'Longitude: ${cartridge.latLng.longitude}\n'; - contents += 'Player: ${cartridge.playerName}\n'; - contents += 'Author: ${cartridge.author}\n'; - contents += 'Type: ${cartridge.typeOfCartridge}\n'; - contents += 'Device: ${cartridge.recommendDevice}\n'; - contents += 'Version: ${cartridge.version}\n'; - contents += '\n'; - contents += 'Completion-Code: ${cartridge.completionCode}\n'; - contents += '\n'; - contents += 'ItemCount: ${cartridge.mediaObjects.length}\n'; - var infoFile = File('${export.path}/cartridge_info.txt'); - await infoFile.writeAsString(contents); + var contents = ''; + contents += 'Cartridge-Id: ${cartridge.cartridgeGuid}\n'; + contents += 'Name: ${cartridge.cartridgeName}\n'; + contents += 'Description: ${cartridge.cartridgeDesc}\n'; + contents += 'StartLocation: ${cartridge.startLocationDesc}\n'; + contents += 'Latitude: ${cartridge.latLng.latitude}\n'; + contents += 'Longitude: ${cartridge.latLng.longitude}\n'; + contents += 'Player: ${cartridge.playerName}\n'; + contents += 'Author: ${cartridge.author}\n'; + contents += 'Type: ${cartridge.typeOfCartridge}\n'; + contents += 'Device: ${cartridge.recommendDevice}\n'; + contents += 'Version: ${cartridge.version}\n'; + contents += '\n'; + contents += 'Completion-Code: ${cartridge.completionCode}\n'; + contents += '\n'; + contents += 'ItemCount: ${cartridge.mediaCount}\n'; + var infoFile = File('${export.path}/cartridge_info.txt'); + await infoFile.writeAsString(contents); - cartridge.mediaObjects.forEach((index, data) async { - var objectFile = File('${export.path}/object_$index.${data.objectType}'); - await objectFile.writeAsBytes(data.data); - }); + for (var index = 0; index < cartridge.mediaCount; index++) { + final data = cartridge.getMedia(index); + if (data != null) { + var objectFile = + File('${export.path}/object_$index.${data.objectType}'); + await objectFile.writeAsBytes(data.data); + } + } + } else { + print("The cartridge is invalid"); + } } diff --git a/lib/src/binary_reader.dart b/lib/src/binary_reader.dart index b7d9b87..512ab05 100644 --- a/lib/src/binary_reader.dart +++ b/lib/src/binary_reader.dart @@ -23,7 +23,7 @@ class BinaryReader { /// Initialise the [BinaryReader] with data and endian, so the reader knows how to /// interpret the bytes. - BinaryReader({ByteData byteData, Endian endian}) + BinaryReader({required ByteData byteData, Endian endian = Endian.little}) : _byteData = byteData, _endian = endian, _index = 0; diff --git a/lib/src/latlng.dart b/lib/src/latlng.dart index 839a434..ab0e271 100644 --- a/lib/src/latlng.dart +++ b/lib/src/latlng.dart @@ -32,11 +32,20 @@ class LatLng { return _format(_longitude, 'EW'); } + @override + String toString() { + return "$latitude $longitude".trim(); + } + String _format(double value, String suffix) { final isNegative = value < 0; value = value.abs(); final degrees = value.floor(); + if (degrees == 360) { + return ""; + } + final minutes = (value - degrees) * 60.0; var result = ''; diff --git a/lib/src/models/cartridge.dart b/lib/src/models/cartridge.dart index b24bb17..8d0e5d1 100644 --- a/lib/src/models/cartridge.dart +++ b/lib/src/models/cartridge.dart @@ -12,14 +12,11 @@ * all copies or substantial portions of the Software. */ -import 'package:meta/meta.dart'; - import '../../freewig.dart'; import '../binary_reader.dart'; import 'media.dart'; /// A [Cartridge] represents the GWC file -@immutable class Cartridge { /// The altitude of the start coordinate final double altitude; @@ -60,16 +57,20 @@ class Cartridge { /// Completion code (can be encrypted with lua) final String completionCode; - /// Map of all [Media] objects - final Map mediaObjects; - final int _splashScreenId; final int _smallIconId; + final BinaryReader _source; + + final Map _references; + + var _lastObject = -1; + + Media? _lastMedia; + Cartridge._( this.cartridgeGuid, - this.mediaObjects, this.altitude, this.author, this.cartridgeDesc, @@ -84,11 +85,13 @@ class Cartridge { this.startLocationDesc, this.typeOfCartridge, this.version, + this._references, + this._source, ); /// Reading the GWC file and create a [Cartridge] or null in case of any /// parsing error. - factory Cartridge(BinaryReader reader) { + static Cartridge? create(BinaryReader reader) { try { final count = reader.getUShort(); final references = {}; @@ -129,14 +132,8 @@ class Cartridge { final completionCode = reader.getASCIIZ(); - // initialise objects after cartridge data is loaded - final objects = {}; - references.forEach((index, address) => - {objects.putIfAbsent(index, () => Media(reader, index, address))}); - return Cartridge._( cartridgeGuid, - objects, altitude, author, cartridgeDesc, @@ -154,6 +151,8 @@ class Cartridge { startLocationDesc, typeOfCartridge, version, + references, + reader, ); } on Exception catch (ex) { print('Exception: $ex'); @@ -166,22 +165,42 @@ class Cartridge { identical(this, other) || other is Cartridge && runtimeType == other.runtimeType && - cartridgeGuid == other.cartridgeGuid && - mediaObjects == other.mediaObjects; + cartridgeGuid == other.cartridgeGuid; @override - int get hashCode => cartridgeGuid.hashCode ^ mediaObjects.hashCode; + int get hashCode => cartridgeGuid.hashCode; + + /// get Media with objectId or null, if not available + Media? getMedia(int objectId) { + if (_lastObject == objectId && _lastMedia != null) { + return _lastMedia!; + } + + if (_references.containsKey(objectId)) { + final address = _references[objectId]; + final media = Media.create(_source, objectId, address!); + + if (media != null) { + if (media.data.length < 128000) { + _lastObject = objectId; + _lastMedia = media; + } + + return media; + } + } + return null; + } + + /// get the count of attached media + int get mediaCount => _references.length; /// get splash screen media or null if not available - Media get splashScreen => mediaObjects.containsKey(_splashScreenId) - ? mediaObjects[_splashScreenId] - : null; + Media? get splashScreen => getMedia(_splashScreenId); /// get small icon media or null if not available - Media get smallIcon => mediaObjects.containsKey(_smallIconId) - ? mediaObjects[_smallIconId] - : null; + Media? get smallIcon => getMedia(_smallIconId); /// get luac media or null if not available - Media get luac => mediaObjects.containsKey(0) ? mediaObjects[0] : null; + Media? get luac => getMedia(0); } diff --git a/lib/src/models/media.dart b/lib/src/models/media.dart index 99ac8ec..e626f37 100644 --- a/lib/src/models/media.dart +++ b/lib/src/models/media.dart @@ -34,10 +34,10 @@ class Media { /// /// It starts at address (from [SeekOrigin.begin]) and uses the data for /// interpret the data as media. If it can be read, the return will be a - /// [Media] object. In case of an exception the result will be null. - factory Media(BinaryReader reader, int objectId, int address) { + /// [Media] object. In case of an exception or empty data the result will be null. + static Media? create(BinaryReader reader, int objectId, int address) { try { - Uint8List data; + Uint8List? data; var objectType = 0; reader.seek(address, SeekOrigin.begin); if (objectId == 0) { @@ -48,8 +48,11 @@ class Media { data = _readMediaData(reader); } } - final object = Media._(_getObjectType(objectType), data); - return object; + if (data != null) { + final object = Media._(_getObjectType(objectType), data); + return object; + } + return null; } on Exception catch (ex) { print('Exception: $ex'); return null; diff --git a/lib/src/parser.dart b/lib/src/parser.dart index 1016187..f5b777c 100644 --- a/lib/src/parser.dart +++ b/lib/src/parser.dart @@ -20,7 +20,7 @@ import 'models/cartridge.dart'; /// Parses a byte list and create a [Cartridge] /// /// If the byte list is a valid GWC file, the result will be [Cartridge] or null, if the parser failed. -Cartridge parseData(Uint8List bytes) { +Cartridge? parseData(Uint8List bytes) { try { const header = {0x02, 0x0a, 0x43, 0x41, 0x52, 0x54, 0x00}; // magic header final reader = BinaryReader( @@ -34,7 +34,7 @@ Cartridge parseData(Uint8List bytes) { } } - return Cartridge(reader); + return Cartridge.create(reader); } on Exception catch (_) { return null; } diff --git a/pubspec.yaml b/pubspec.yaml index a07e373..1a7a493 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,11 +1,11 @@ name: freewig -version: 0.1.2+2 +version: 0.2.0 description: > Freewig can be used to extract the meta data and media files from Wherigo Cartridges homepage: https://github.com/mars3142/freewig environment: - sdk: '>=2.7.0 <3.0.0' + sdk: '>=2.12.0 <3.0.0' dependencies: meta: ^1.1.8 diff --git a/test/gwc_test.dart b/test/gwc_test.dart index a508d90..f07a2c7 100644 --- a/test/gwc_test.dart +++ b/test/gwc_test.dart @@ -15,13 +15,13 @@ import 'dart:async'; import 'dart:io'; -import 'package:test/test.dart'; import 'package:freewig/freewig.dart'; +import 'package:test/test.dart'; -Future parseFile(File file) async { +Future parseFile(File file) async { final bytes = await file.readAsBytes(); final completer = Completer(); - await Future(() => parseData(bytes)) + await Future(() => parseData(bytes)) .then(completer.complete) .catchError(completer.completeError); return completer.future; @@ -29,27 +29,26 @@ Future parseFile(File file) async { void main() { group('Cartridge TESTING.GWC', () { - Cartridge cartridgeData; + Cartridge? cartridgeData; setUp(() async { var file = File("testing.gwc"); cartridgeData = await parseFile(file); }); - test('Cartridge not empty', () { - expect(cartridgeData != null, isTrue); - }); - test('Cartridge name is "TESTING"', () { - expect(cartridgeData.cartridgeName, "TESTING"); + expect(cartridgeData != null, isTrue); + expect(cartridgeData!.cartridgeName, "TESTING"); }); test('Completion Code is "ABCDEFGHIJKLMNOP"', () { - expect(cartridgeData.completionCode, "ABCDEFGHIJKLMNOP"); + expect(cartridgeData != null, isTrue); + expect(cartridgeData!.completionCode, "ABCDEFGHIJKLMNOP"); }); test('Cartridge has 2 objects', () { - expect(cartridgeData.mediaObjects.length, 2); + expect(cartridgeData != null, isTrue); + expect(cartridgeData?.mediaCount, 2); }); }); } diff --git a/test/latlng_test.dart b/test/latlng_test.dart index d62d6e4..e64ceca 100644 --- a/test/latlng_test.dart +++ b/test/latlng_test.dart @@ -12,8 +12,8 @@ * all copies or substantial portions of the Software. */ -import 'package:test/test.dart'; import 'package:freewig/freewig.dart'; +import 'package:test/test.dart'; void main() { group('LatLng testing', () {