package zim import ( "encoding/binary" "github.com/pkg/errors" ) type Namespace string const ( V6NamespaceContent = "C" V6NamespaceMetadata = "M" V6NamespaceWellKnown = "W" V6NamespaceSearch = "X" ) const ( V5NamespaceLayout = "-" V5NamespaceArticle = "A" V5NamespaceArticleMetadata = "B" V5NamespaceImageFile = "I" V5NamespaceImageText = "J" V5NamespaceMetadata = "M" V5NamespaceCategoryText = "U" V5NamespaceCategoryArticleList = "V" V5NamespaceCategoryPerArticle = "W" V5NamespaceSearch = "X" ) type Entry interface { Redirect() (*ContentEntry, error) Namespace() Namespace URL() string Title() string } type BaseEntry struct { mimeTypeIndex uint16 namespace Namespace url string title string reader *Reader } func (e *BaseEntry) Namespace() Namespace { return e.namespace } func (e *BaseEntry) Title() string { if e.title == "" { return e.url } return e.title } func (e *BaseEntry) URL() string { return e.url } func (r *Reader) parseBaseEntry(offset int64) (*BaseEntry, error) { entry := &BaseEntry{ reader: r, } data := make([]byte, 2) if err := r.readRange(offset, data); err != nil { return nil, errors.WithStack(err) } mimeTypeIndex, err := readUint16(data, binary.LittleEndian) if err != nil { return nil, errors.WithStack(err) } entry.mimeTypeIndex = mimeTypeIndex data = make([]byte, 1) if err := r.readRange(offset+3, data); err != nil { return nil, errors.WithStack(err) } entry.namespace = Namespace(data[0]) return entry, nil } type RedirectEntry struct { *BaseEntry redirectIndex uint32 } func (e *RedirectEntry) Redirect() (*ContentEntry, error) { if e.redirectIndex >= uint32(len(e.reader.urlIndex)) { return nil, errors.Wrapf(ErrInvalidEntryIndex, "entry index '%d' out of bounds", e.redirectIndex) } entryPtr := e.reader.urlIndex[e.redirectIndex] entry, err := e.reader.parseEntryAt(int64(entryPtr)) if err != nil { return nil, errors.WithStack(err) } entry, err = entry.Redirect() if err != nil { return nil, errors.WithStack(err) } contentEntry, ok := entry.(*ContentEntry) if !ok { return nil, errors.WithStack(ErrInvalidRedirect) } return contentEntry, nil } func (r *Reader) parseRedirectEntry(offset int64, base *BaseEntry) (*RedirectEntry, error) { entry := &RedirectEntry{ BaseEntry: base, } data := make([]byte, 4) if err := r.readRange(offset+8, data); err != nil { return nil, errors.WithStack(err) } redirectIndex, err := readUint32(data, binary.LittleEndian) if err != nil { return nil, errors.WithStack(err) } entry.redirectIndex = redirectIndex url, read, err := r.readStringAt(offset + 12) if err != nil { return nil, errors.WithStack(err) } entry.url = url title, _, err := r.readStringAt(offset + 12 + read) if err != nil { return nil, errors.WithStack(err) } entry.title = title return entry, nil }