136 lines
2.5 KiB
Go
136 lines
2.5 KiB
Go
package zim
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
type Entry interface {
|
|
Redirect() (*ContentEntry, error)
|
|
Namespace() Namespace
|
|
URL() string
|
|
FullURL() 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 (e *BaseEntry) FullURL() string {
|
|
return toFullURL(e.Namespace(), e.URL())
|
|
}
|
|
|
|
func (r *Reader) parseBaseEntry(offset int64) (*BaseEntry, error) {
|
|
entry := &BaseEntry{
|
|
reader: r,
|
|
}
|
|
|
|
data := make([]byte, 3)
|
|
if err := r.readRange(offset, data); err != nil {
|
|
return nil, errors.WithStack(err)
|
|
}
|
|
|
|
mimeTypeIndex, err := readUint16(data[0:2], binary.LittleEndian)
|
|
if err != nil {
|
|
return nil, errors.WithStack(err)
|
|
}
|
|
|
|
entry.mimeTypeIndex = mimeTypeIndex
|
|
entry.namespace = Namespace(data[2])
|
|
|
|
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(ErrInvalidIndex, "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
|
|
|
|
strs, _, err := r.readStringsAt(offset+12, 2, 1024)
|
|
if err != nil {
|
|
return nil, errors.WithStack(err)
|
|
}
|
|
|
|
if len(strs) > 0 {
|
|
entry.url = strs[0]
|
|
}
|
|
|
|
if len(strs) > 1 {
|
|
entry.title = strs[1]
|
|
}
|
|
|
|
return entry, nil
|
|
}
|
|
|
|
func toFullURL(ns Namespace, url string) string {
|
|
if ns == "\x00" {
|
|
return url
|
|
}
|
|
|
|
return fmt.Sprintf("%s/%s", ns, url)
|
|
}
|