151 lines
2.9 KiB
Go
151 lines
2.9 KiB
Go
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
|
|
}
|