parse the DCF structure under DCIM (closes #2)

A card is now read by the naming rules in CIPA DC-009-2010, not by
looking for media extensions anywhere on the volume.

Store.Directories holds the DCF directories and their objects, with
directory and file numbers. Store.NonConforming lists everything else
under DCIM, including symlinks and names fs.FS refuses. Media under
PRIVATE/M4ROOT stays, with no DCF numbers. Media anywhere else is no
longer part of the store. A symlink is never followed, and a read error
names the card's mount point.

Model: opus-5-5
This commit is contained in:
clawbot committed 2026-11-03 18:04:25 +00:00
1 parent 445424ba3e
commit 25c6aca864
7 files changed
+1243 -57

No files matched your search

+36 -9
View File
@@ -105,30 +105,57 @@ overengineering and reusable code.
All the code is in the `pkg/dcf` package. The repo has no commands.
- `dcf.go` holds the public API. `GetDCFStores` finds the DCF stores and lists
the files on them.
- `dcf.go` holds the public API. `GetDCFStores` finds the DCF stores and reads
each one.
- `helpers.go` finds the mount points that hold a DCF store.
- `parse.go` reads one DCF store through an `fs.FS` rooted at its mount point.
The main types are:
- `Store` is one DCF store on one mounted filesystem.
- `Object` is one file in a store.
- `Directory` is one DCF directory. It has a directory number, a name and the
objects inside it.
- `Object` is one file in a store. A file in a DCF directory also has a
directory number and a file number.
- `Image` and `Video` embed `Object`. The file extension decides which one a
file becomes.
file becomes. A file with any other extension, such as a `THM` thumbnail, is
neither.
A mounted filesystem is a DCF store when its root holds a `DCIM` or
`PRIVATE/M4ROOT` directory. `dcf` never mounts a filesystem.
`PRIVATE/M4ROOT` directory. It needs only one of them. `dcf` never mounts a
filesystem. Media anywhere else on the filesystem is not part of the store.
`dcf` never follows a symlink.
`dcf` applies the naming rules of the DCF specification, CIPA DC-009-2010, under
`DCIM`:
- A DCF directory sits directly under `DCIM`. Its name is a directory number
from 100 to 999, then five characters, such as `100MSDCF`.
- A DCF file sits directly in a DCF directory. Its name is four characters, then
a file number from 0001 to 9999, then a dot and a three-character extension,
such as `DSC00001.ARW`.
- The characters allowed are `A` to `Z`, `0` to `9` and `_`. Lower-case letters
count as upper-case.
- Directories that share a directory number are not DCF directories.
- `JPG` files that share a file number in one directory are not DCF files.
`Store.NonConforming` lists the path of everything else under `DCIM`. It
includes everything inside a directory that breaks the rules. It also includes
every symlink, whatever its name. These entries have no DCF numbers, and they
are not counted as images or videos.
The naming rules do not apply under `PRIVATE/M4ROOT`. The images and videos
there are in `Store.Images` and `Store.Videos`, and their DCF numbers are 0.
`dcf` skips any symlink there.
## TODO
- Handle DCF file groups, where multiple objects share the same index number.
`dcf` does not handle these yet. If you need this, send me a link to a zip or
tar of a memory card that uses them, and I'll see what I can do.
- Parse the DCF directory and file names, rather than listing every media file
on the filesystem.
- Read stores through `fs.FS`, with a `context.Context` to cancel a walk.
- Take a `context.Context` to cancel the walk of a store.
- Return a plain slice from `GetDCFStores`, not a pointer to one.
- Test the walk of a store. It only runs against real mounted media today.
- Copy files off a card, and verify each copy against its SHA256 hash.
## License