JJ_MKVMaker/lib/core/file_attributes.dart

50 lines
2.3 KiB
Dart

import 'dart:ffi';
import 'dart:io';
import 'dart:typed_data';
import 'package:ffi/ffi.dart';
import 'package:path/path.dart' as p;
import '../l10n/tr.dart';
/// X-plore P3 보충 (관리자 지시 10/18): 파일 · 폴더의 숨김 · 읽기 전용 · 시스템 속성과 만든 때 - 보기만 (바꾸지 않음).
/// Windows 는 GetFileAttributesExW (kernel32), 그 밖 (Android) 은 이름이 점으로 시작하면 숨김 · 만든 때는 모름.
class FileAttributes {
final bool hidden;
final bool readOnly;
final bool system;
/// 만든 때 (모르면 null)
final DateTime? created;
const FileAttributes({this.hidden = false, this.readOnly = false, this.system = false, this.created});
/// 화면에 보일 속성 이름들 (없으면 빈 목록)
List<String> get labels => [if (hidden) tr('숨김'), if (readOnly) tr('읽기 전용'), if (system) tr('시스템')];
}
typedef _GetAttrsExC = Int32 Function(Pointer<Utf16> name, Int32 level, Pointer<Uint8> info);
typedef _GetAttrsExD = int Function(Pointer<Utf16> name, int level, Pointer<Uint8> info);
/// [path] 의 속성 (읽지 못하면 null). 로컬 경로만 (원격은 부르지 않는다)
FileAttributes? readFileAttributes(String path) {
if (!Platform.isWindows) return FileAttributes(hidden: p.basename(path).startsWith('.'));
final name = path.toNativeUtf16();
// WIN32_FILE_ATTRIBUTE_DATA: DWORD 속성 · FILETIME 만든 · 열어 본 · 바뀐 때 · DWORD 크기 (위 · 아래) = 36 바이트
final info = calloc<Uint8>(36);
try {
final f = DynamicLibrary.open('kernel32.dll').lookupFunction<_GetAttrsExC, _GetAttrsExD>('GetFileAttributesExW');
if (f(name, 0 /* GetFileExInfoStandard */, info) == 0) return null;
final b = ByteData.sublistView(info.asTypedList(36));
final attrs = b.getUint32(0, Endian.little);
final ft = b.getUint32(4, Endian.little) | (b.getUint32(8, Endian.little) << 32);
// FILETIME: 1601-01-01 부터 100ns 단위
final created = ft == 0 ? null : DateTime.fromMicrosecondsSinceEpoch(ft ~/ 10 - 11644473600000000, isUtc: true).toLocal();
return FileAttributes(hidden: attrs & 0x2 != 0, readOnly: attrs & 0x1 != 0, system: attrs & 0x4 != 0, created: created);
} catch (_) {
return null;
} finally {
calloc.free(name);
calloc.free(info);
}
}