cl-olefs

OLE File System tools for Common Lisp
git clone https://logand.com/git/cl-olefs.git/
Log | Files | Refs

commit 54ec6bcd626c46728bee10d20b9ae78e7186eb43
parent 918afce41d645676f46db099cd818b034a2c5228
Author: Tomas Hlavaty <tom@logand.com>
Date:   Sat, 21 May 2011 18:47:07 +0200

png and jpeg extraction from powerpoint files works

Diffstat:
Acdef.lisp | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aenums.lisp | 220+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mole.asd | 7+++++--
Mole.lisp | 258++++++++++++++++++++++++++++++++++++++++---------------------------------------
Apackage.lisp | 2++
5 files changed, 414 insertions(+), 129 deletions(-)

diff --git a/cdef.lisp b/cdef.lisp @@ -0,0 +1,56 @@ +(in-package :ole) + +(deftype achar () '(unsigned-byte 8)) +(deftype ubyte () '(unsigned-byte 8)) +(deftype ushort () '(unsigned-byte 16)) +(deftype wchar () '(unsigned-byte 16)) +(deftype dword () '(unsigned-byte 32)) +(deftype ulonglong () '(unsigned-byte 64)) +(deftype filetime () '(unsigned-byte 64)) +(deftype guid () '(vector ubyte 16)) + +(defun slot-type-definition (type) + (if (atom type) + type + (destructuring-bind (type1 size) type + (list 'vector type1 (if (numberp size) size '*))))) + +(defun slot-type-definition-for-reader (type) + (if (atom type) + `(',type) + (destructuring-bind (type1 size) type + (if (numberp size) + `('(,type1 ,size)) + `((list ',type1 ,size)))))) + +(defun slot-reader-let-definition (name type &key compute always member) + (list + name + (flet ((value () + (cond + (compute compute) + (t `(read-value ,@(slot-type-definition-for-reader type) stream))))) + (cond + (always `(let ((x ,(value))) (assert (equal x ,always)) x)) + (member `(let ((x ,(value))) (assert (member x ,member)) x)) + (t (value)))))) + +(defmacro define-structure (name options &rest slots) + (declare (ignore options)) + `(progn + (defstruct (,name (:conc-name ,(intern (format nil "~a." name)))) + ,@(loop + for slot in slots + collect (list (car slot) + nil + :type (slot-type-definition (cadr slot))))) + (defun ,(intern (format nil "READ-~a" name)) (stream) + (let* (,@(loop + for slot in slots + collect (apply 'slot-reader-let-definition slot))) + (,(intern (format nil "MAKE-~a" name)) + ,@(loop + for slot in slots + appending (list + (intern (symbol-name (car slot)) :keyword) + (car slot)))))))) diff --git a/enums.lisp b/enums.lisp @@ -0,0 +1,220 @@ +(in-package :ole) + +(defconstant RT_Document #x03E8) +(defconstant RT_DocumentAtom #x03E9) +(defconstant RT_EndDocumentAtom #x03EA) +(defconstant RT_Slide #x03EE) +(defconstant RT_SlideAtom #x03EF) +(defconstant RT_Notes #x03F0) +(defconstant RT_NotesAtom #x03F1) +(defconstant RT_Environment #x03F2) +(defconstant RT_SlidePersistAtom #x03F3) +(defconstant RT_MainMaster #x03F8) +(defconstant RT_SlideShowSlideInfoAtom #x03F9) +(defconstant RT_SlideViewInfo #x03FA) +(defconstant RT_GuideAtom #x03FB) +(defconstant RT_ViewInfoAtom #x03FD) +(defconstant RT_SlideViewInfoAtom #x03FE) +(defconstant RT_VbaInfo #x03FF) +(defconstant RT_VbaInfoAtom #x0400) +(defconstant RT_SlideShowDocInfoAtom #x0401) +(defconstant RT_Summary #x0402) +(defconstant RT_DocRoutingSlipAtom #x0406) +(defconstant RT_OutlineViewInfo #x0407) +(defconstant RT_SorterViewInfo #x0408) +(defconstant RT_ExternalObjectList #x0409) +(defconstant RT_ExternalObjectListAtom #x040A) +(defconstant RT_DrawingGroup #x040B) +(defconstant RT_Drawing #x040C) +(defconstant RT_GridSpacing10Atom #x040D) +(defconstant RT_RoundTripTheme12Atom #x040E) +(defconstant RT_RoundTripColorMapping12Atom #x040F) +(defconstant RT_NamedShows #x0410) +(defconstant RT_NamedShow #x0411) +(defconstant RT_NamedShowSlidesAtom #x0412) +(defconstant RT_NotesTextViewInfo9 #x0413) +(defconstant RT_NormalViewSetInfo9 #x0414) +(defconstant RT_NormalViewSetInfo9Atom #x0415) +(defconstant RT_RoundTripOriginalMainMasterId12Atom #x041C) +(defconstant RT_RoundTripCompositeMasterId12Atom #x041D) +(defconstant RT_RoundTripContentMasterInfo12Atom #x041E) +(defconstant RT_RoundTripShapeId12Atom #x041F) +(defconstant RT_RoundTripHFPlaceholder12Atom #x0420) +(defconstant RT_RoundTripContentMasterId12Atom #x0422) +(defconstant RT_RoundTripOArtTextStyles12Atom #x0423) +(defconstant RT_RoundTripHeaderFooterDefaults12Atom #x0424) +(defconstant RT_RoundTripDocFlags12Atom #x0425) +(defconstant RT_RoundTripShapeCheckSumForCL12Atom #x0426) +(defconstant RT_RoundTripNotesMasterTextStyles12Atom #x0427) +(defconstant RT_RoundTripCustomTableStyles12Atom #x0428) +(defconstant RT_List #x07D0) +(defconstant RT_FontCollection #x07D5) +(defconstant RT_FontCollection10 #x07D6) +(defconstant RT_BookmarkCollection #x07E3) +(defconstant RT_SoundCollection #x07E4) +(defconstant RT_SoundCollectionAtom #x07E5) +(defconstant RT_Sound #x07E6) +(defconstant RT_SoundDataBlob #x07E7) +(defconstant RT_BookmarkSeedAtom #x07E9) +(defconstant RT_ColorSchemeAtom #x07F0) +(defconstant RT_BlipCollection9 #x07F8) +(defconstant RT_BlipEntity9Atom #x07F9) +(defconstant RT_ExternalObjectRefAtom #x0BC1) +(defconstant RT_PlaceholderAtom #x0BC3) +(defconstant RT_ShapeAtom #x0BDB) +(defconstant RT_ShapeFlags10Atom #x0BDC) +(defconstant RT_RoundTripNewPlaceholderId12Atom #x0BDD) +(defconstant RT_OutlineTextRefAtom #x0F9E) +(defconstant RT_TextHeaderAtom #x0F9F) +(defconstant RT_TextCharsAtom #x0FA0) +(defconstant RT_StyleTextPropAtom #x0FA1) +(defconstant RT_MasterTextPropAtom #x0FA2) +(defconstant RT_TextMasterStyleAtom #x0FA3) +(defconstant RT_TextCharFormatExceptionAtom #x0FA4) +(defconstant RT_TextParagraphFormatExceptionAtom #x0FA5) +(defconstant RT_TextRulerAtom #x0FA6) +(defconstant RT_TextBookmarkAtom #x0FA7) +(defconstant RT_TextBytesAtom #x0FA8) +(defconstant RT_TextSpecialInfoDefaultAtom #x0FA9) +(defconstant RT_TextSpecialInfoAtom #x0FAA) +(defconstant RT_DefaultRulerAtom #x0FAB) +(defconstant RT_StyleTextProp9Atom #x0FAC) +(defconstant RT_TextMasterStyle9Atom #x0FAD) +(defconstant RT_OutlineTextProps9 #x0FAE) +(defconstant RT_OutlineTextPropsHeader9Atom #x0FAF) +(defconstant RT_TextDefaults9Atom #x0FB0) +(defconstant RT_StyleTextProp10Atom #x0FB1) +(defconstant RT_TextMasterStyle10Atom #x0FB2) +(defconstant RT_OutlineTextProps10 #x0FB3) +(defconstant RT_TextDefaults10Atom #x0FB4) +(defconstant RT_OutlineTextProps11 #x0FB5) +(defconstant RT_StyleTextProp11Atom #x0FB6) +(defconstant RT_FontEntityAtom #x0FB7) +(defconstant RT_FontEmbedDataBlob #x0FB8) +(defconstant RT_CString #x0FBA) +(defconstant RT_MetaFile #x0FC1) +(defconstant RT_ExternalOleObjectAtom #x0FC3) +(defconstant RT_Kinsoku #x0FC8) +(defconstant RT_Handout #x0FC9) +(defconstant RT_ExternalOleEmbed #x0FCC) +(defconstant RT_ExternalOleEmbedAtom #x0FCD) +(defconstant RT_ExternalOleLink #x0FCE) +(defconstant RT_BookmarkEntityAtom #x0FD0) +(defconstant RT_ExternalOleLinkAtom #x0FD1) +(defconstant RT_KinsokuAtom #x0FD2) +(defconstant RT_ExternalHyperlinkAtom #x0FD3) +(defconstant RT_ExternalHyperlink #x0FD7) +(defconstant RT_SlideNumberMetaCharAtom #x0FD8) +(defconstant RT_HeadersFooters #x0FD9) +(defconstant RT_HeadersFootersAtom #x0FDA) +(defconstant RT_TextInteractiveInfoAtom #x0FDF) +(defconstant RT_ExternalHyperlink9 #x0FE4) +(defconstant RT_RecolorInfoAtom #x0FE7) +(defconstant RT_ExternalOleControl #x0FEE) +(defconstant RT_SlideListWithText #x0FF0) +(defconstant RT_AnimationInfoAtom #x0FF1) +(defconstant RT_InteractiveInfo #x0FF2) +(defconstant RT_InteractiveInfoAtom #x0FF3) +(defconstant RT_UserEditAtom #x0FF5) +(defconstant RT_CurrentUserAtom #x0FF6) +(defconstant RT_DateTimeMetaCharAtom #x0FF7) +(defconstant RT_GenericDateMetaCharAtom #x0FF8) +(defconstant RT_HeaderMetaCharAtom #x0FF9) +(defconstant RT_FooterMetaCharAtom #x0FFA) +(defconstant RT_ExternalOleControlAtom #x0FFB) +(defconstant RT_ExternalMediaAtom #x1004) +(defconstant RT_ExternalVideo #x1005) +(defconstant RT_ExternalAviMovie #x1006) +(defconstant RT_ExternalMciMovie #x1007) +(defconstant RT_ExternalMidiAudio #x100D) +(defconstant RT_ExternalCdAudio #x100E) +(defconstant RT_ExternalWavAudioEmbedded #x100F) +(defconstant RT_ExternalWavAudioLink #x1010) +(defconstant RT_ExternalOleObjectStg #x1011) +(defconstant RT_ExternalCdAudioAtom #x1012) +(defconstant RT_ExternalWavAudioEmbeddedAtom #x1013) +(defconstant RT_AnimationInfo #x1014) +(defconstant RT_RtfDateTimeMetaCharAtom #x1015) +(defconstant RT_ExternalHyperlinkFlagsAtom #x1018) +(defconstant RT_ProgTags #x1388) +(defconstant RT_ProgStringTag #x1389) +(defconstant RT_ProgBinaryTag #x138A) +(defconstant RT_BinaryTagDataBlob #x138B) +(defconstant RT_PrintOptionsAtom #x1770) +(defconstant RT_PersistDirectoryAtom #x1772) +(defconstant RT_PresentationAdvisorFlags9Atom #x177A) +(defconstant RT_HtmlDocInfo9Atom #x177B) +(defconstant RT_HtmlPublishInfoAtom #x177C) +(defconstant RT_HtmlPublishInfo9 #x177D) +(defconstant RT_BroadcastDocInfo9 #x177E) +(defconstant RT_BroadcastDocInfo9Atom #x177F) +(defconstant RT_EnvelopeFlags9Atom #x1784) +(defconstant RT_EnvelopeData9Atom #x1785) +(defconstant RT_VisualShapeAtom #x2AFB) +(defconstant RT_HashCodeAtom #x2B00) +(defconstant RT_VisualPageAtom #x2B01) +(defconstant RT_BuildList #x2B02) +(defconstant RT_BuildAtom #x2B03) +(defconstant RT_ChartBuild #x2B04) +(defconstant RT_ChartBuildAtom #x2B05) +(defconstant RT_DiagramBuild #x2B06) +(defconstant RT_DiagramBuildAtom #x2B07) +(defconstant RT_ParaBuild #x2B08) +(defconstant RT_ParaBuildAtom #x2B09) +(defconstant RT_LevelInfoAtom #x2B0A) +(defconstant RT_RoundTripAnimationAtom12Atom #x2B0B) +(defconstant RT_RoundTripAnimationHashAtom12Atom #x2B0D) +(defconstant RT_Comment10 #x2EE0) +(defconstant RT_Comment10Atom #x2EE1) +(defconstant RT_CommentIndex10 #x2EE4) +(defconstant RT_CommentIndex10Atom #x2EE5) +(defconstant RT_LinkedShape10Atom #x2EE6) +(defconstant RT_LinkedSlide10Atom #x2EE7) +(defconstant RT_SlideFlags10Atom #x2EEA) +(defconstant RT_SlideTime10Atom #x2EEB) +(defconstant RT_DiffTree10 #x2EEC) +(defconstant RT_Diff10 #x2EED) +(defconstant RT_Diff10Atom #x2EEE) +(defconstant RT_SlideListTableSize10Atom #x2EEF) +(defconstant RT_SlideListEntry10Atom #x2EF0) +(defconstant RT_SlideListTable10 #x2EF1) +(defconstant RT_CryptSession10Container #x2F14) +(defconstant RT_FontEmbedFlags10Atom #x32C8) +(defconstant RT_FilterPrivacyFlags10Atom #x36B0) +(defconstant RT_DocToolbarStates10Atom #x36B1) +(defconstant RT_PhotoAlbumInfo10Atom #x36B2) +(defconstant RT_SmartTagStore11Container #x36B3) +(defconstant RT_RoundTripSlideSyncInfo12 #x3714) +(defconstant RT_RoundTripSlideSyncInfoAtom12 #x3715) +(defconstant RT_TimeConditionContainer #xF125) +(defconstant RT_TimeNode #xF127) +(defconstant RT_TimeCondition #xF128) +(defconstant RT_TimeModifier #xF129) +(defconstant RT_TimeBehaviorContainer #xF12A) +(defconstant RT_TimeAnimateBehaviorContainer #xF12B) +(defconstant RT_TimeColorBehaviorContainer #xF12C) +(defconstant RT_TimeEffectBehaviorContainer #xF12D) +(defconstant RT_TimeMotionBehaviorContainer #xF12E) +(defconstant RT_TimeRotationBehaviorContainer #xF12F) +(defconstant RT_TimeScaleBehaviorContainer #xF130) +(defconstant RT_TimeSetBehaviorContainer #xF131) +(defconstant RT_TimeCommandBehaviorContainer #xF132) +(defconstant RT_TimeBehavior #xF133) +(defconstant RT_TimeAnimateBehavior #xF134) +(defconstant RT_TimeColorBehavior #xF135) +(defconstant RT_TimeEffectBehavior #xF136) +(defconstant RT_TimeMotionBehavior #xF137) +(defconstant RT_TimeRotationBehavior #xF138) +(defconstant RT_TimeScaleBehavior #xF139) +(defconstant RT_TimeSetBehavior #xF13A) +(defconstant RT_TimeCommandBehavior #xF13B) +(defconstant RT_TimeClientVisualElement #xF13C) +(defconstant RT_TimePropertyList #xF13D) +(defconstant RT_TimeVariantList #xF13E) +(defconstant RT_TimeAnimationValueList #xF13F) +(defconstant RT_TimeIterateData #xF140) +(defconstant RT_TimeSequenceData #xF141) +(defconstant RT_TimeVariant #xF142) +(defconstant RT_TimeAnimationValue #xF143) +(defconstant RT_TimeExtTimeNodeContainer #xF144) +(defconstant RT_TimeSlaveContainer #xF145) diff --git a/ole.asd b/ole.asd @@ -6,11 +6,14 @@ (in-package :ole-system) (defsystem :ole - :description "Ole for Common Lisp." + :description "OLE for Common Lisp." :version "" :author "Tomas Hlavaty" :maintainer "Tomas Hlavaty" :licence "" :depends-on (:trivial-gray-streams :alexandria) :serial t - :components ((:file "ole"))) + :components ((:file "package") + (:file "cdef") + (:file "enums") + (:file "ole"))) diff --git a/ole.lisp b/ole.lisp @@ -1,8 +1,7 @@ -(defpackage :ole - (:use :cl)) - (in-package :ole) +;;; MS-CFB Compound File Binary File Format + (defconstant +unused-sector+ 0) (defconstant +maxregsect+ #xfffffffa) (defconstant +difsect+ #xfffffffc) @@ -13,14 +12,6 @@ (defconstant +maxregsig+ #xfffffffa) (defconstant +nostream+ #xffffffff) -(deftype ubyte () '(unsigned-byte 8)) -(deftype ushort () '(unsigned-byte 16)) -(deftype wchar () '(unsigned-byte 16)) -(deftype dword () '(unsigned-byte 32)) -(deftype ulonglong () '(unsigned-byte 64)) -(deftype filetime () '(unsigned-byte 64)) -(deftype guid () '(vector ubyte 16)) - #+nil (defconstant clsid-null (make-array 16 :element-type '(unsigned-byte 8) @@ -30,6 +21,7 @@ (if (atom type) (ecase type (ubyte (read-byte stream)) + (achar (read-byte stream)) (ushort (logior (read-byte stream) (ash (read-byte stream) 8))) (wchar (logior (read-byte stream) @@ -55,24 +47,6 @@ (dotimes (i size x) (setf (aref x i) (read-value element-type stream))))))) -(defmacro define-structure (name options &rest slots) - (declare (ignore options)) - `(progn - (defstruct (,name (:conc-name ,(intern (format nil "~a." name)))) - ,@(loop - for (name2 type) in slots - collect (list name2 - nil - :type (if (atom type) - type - (cons 'vector type))))) - (defun ,(intern (format nil "READ-~a" name)) (stream) - (,(intern (format nil "MAKE-~a" name)) - ,@(loop - for (name2 type) in slots - appending `(,(intern (symbol-name name2) :keyword) - (read-value ',type stream))))))) - (define-structure ole-header () (signature (ubyte 8)) (clsid guid) @@ -157,16 +131,9 @@ (defun sector-chain (fat location) (labels ((rec (x) - (case x - ;;(#.+unused-sector+) - ;;(+maxregsect+) - (#.+difsect+) - (#.+fatsect+) - (#.+endofchain+) - (#.+freesect+) - (t - (assert (and #+nil(< +unused-sector+ x) (<= 0 x +maxregsect+))) - (cons x (rec (aref fat x))))))) + (unless (member x (list +difsect+ +fatsect+ +endofchain+ +freesect+)) + (assert (and #+nil(< +unused-sector+ x) (<= 0 x +maxregsect+))) + (cons x (rec (aref fat x)))))) (rec location))) (defun read-values (array type stream &optional (start 0) end) @@ -262,10 +229,11 @@ :directories directories :mfat-chain mfat-chain :mfat mfat))) - (describe ole-file) + ;;(describe ole-file) (check-ole-header (ole-file.header ole-file)) - (describe header) - (terpri) + ;;(describe header) + ;;(terpri) + #+nil (traverse-directories ole-file (lambda (entry id level) (declare (ignore id)) @@ -279,52 +247,8 @@ (defmacro with-ole-file ((ole-file filename) &body body) `(call-with-ole-file ,filename (lambda (,ole-file) ,@body))) -(defun save-chain (ole-stream chain filename length) - (with-open-file (s filename - :direction :output - :if-does-not-exist :create - :if-exists :supersede - :element-type '(unsigned-byte 8)) - (let ((buf (make-array 512 :element-type '(unsigned-byte 8)))) - (dolist (x chain) - (seek-sector x ole-stream) - (let ((n (read-sequence buf ole-stream))) - (decf length n) - (write-sequence buf s :end (if (plusp length) n (+ length 512)))))))) - -(defun save-entry-stream (ole-file entry filename) - (if (<= (ole-entry.stream-size entry) - (ole-header.mini-stream-cutoff-size (ole-file.header ole-file))) - (save-chain (ole-file.stream ole-file) ;; TODO mini stream, mfat? - (sector-chain (ole-file.fat ole-file) ;; mfat? - (ole-entry.starting-sector-location - (aref (ole-file.directories ole-file) 0))) - filename - (ole-entry.stream-size entry)) - (save-chain (ole-file.stream ole-file) - (sector-chain (ole-file.fat ole-file) - (ole-entry.starting-sector-location entry)) - filename - (ole-entry.stream-size entry)))) - -(defun extract-ole-file (filename) - (with-ole-file (ole-file filename) - (traverse-directories - ole-file - (lambda (entry id level) - (declare (ignore id level)) - (case (ole-entry.object-type entry) - ;;(1 "storage") - (2 ;; stream - (save-entry-stream ole-file - entry - (format nil "/tmp/~a" - (ole-entry-name-to-string - (ole-entry.name entry) - (ole-entry.name-length entry)))))))))) - - -(defclass ole-entry-stream (trivial-gray-streams:fundamental-binary-input-stream) +(defclass ole-entry-stream (trivial-gray-streams:fundamental-binary-input-stream + trivial-gray-streams:trivial-gray-stream-mixin) ((ole-file :initarg :ole-file) (ole-entry :initarg :ole-entry) (offset :initform 0) @@ -358,6 +282,10 @@ (defmethod trivial-gray-streams::stream-element-type ((stream ole-entry-stream)) '(unsigned-byte 8)) +(defmethod trivial-gray-streams:stream-file-position ((stream ole-entry-stream)) + (with-slots (offset) stream + offset)) + (defmethod trivial-gray-streams:stream-read-byte ((stream ole-entry-stream)) (with-slots (ole-file ole-entry offset chain mchain sector buffer size) stream (assert (not (minusp offset))) @@ -388,49 +316,125 @@ (make-instance 'ole-entry-stream :ole-file ,ole-file :ole-entry ,ole-entry) (lambda (,var) ,@body))) - -(define-structure OfficeArtRecordHeader () - (recVer ushort :always 0) - (recInstance ushort :member '(#x46A #x46B #x6E2 #x6E3)) - (recType ushort :always #xF01D) - (recLen ushort)) - -(define-structure OfficeArtBlipJPEG () - ;;(rh OfficeArtRecordHeader) - (rgbUid1 guid) - (rgbUid2 guid ;;:optional '(when (member recInstance '(#x46B #x6E3))) - ) - (tag ubyte) - #+nil(BLIBFileData)) - -(defun extract-ole-file2 (filename) +(defun extract-ole-file (filename &optional (dir "/tmp")) (with-ole-file (ole-file filename) - (traverse-directories - ole-file - (lambda (entry id level) - (declare (ignore id level)) - (case (ole-entry.object-type entry) - ;;(1 "storage") - (2 ;; stream - (let ((entry-name (ole-entry-name-to-string - (ole-entry.name entry) - (ole-entry.name-length entry)))) - (with-ole-entry-stream (in ole-file entry) - (with-open-file (out (format nil "/tmp/a/~a" entry-name) - :direction :output - :if-does-not-exist :create - :if-exists :supersede - :element-type '(unsigned-byte 8)) - (alexandria:copy-stream in out))) - (when (equal "Pictures" entry-name) + (with-open-file (html (format nil "~a/index.html" dir) + :direction :output + :if-does-not-exist :create + :if-exists :supersede + :element-type 'character) + (traverse-directories + ole-file + (lambda (entry id level) + (declare (ignore id level)) + (case (ole-entry.object-type entry) + ;;(1 "storage") + (2 ;; stream + (let ((entry-name (ole-entry-name-to-string + (ole-entry.name entry) + (ole-entry.name-length entry)))) (with-ole-entry-stream (in ole-file entry) - (print (read-OfficeArtRecordHeader in)) - (print (read-value 'guid in)) - (read-value 'ubyte in) - (with-open-file (out "/tmp/a/a.jpeg" + (with-open-file (out (format nil "~a/~a" dir entry-name) :direction :output :if-does-not-exist :create :if-exists :supersede :element-type '(unsigned-byte 8)) - (alexandria:copy-stream in out))))))))))) + (alexandria:copy-stream in out))) + #+nil + (when (equal "Current User" entry-name) + (with-ole-entry-stream (in ole-file entry) + (print (read-record dir in)))) + (when (equal "Pictures" entry-name) + (with-ole-entry-stream (in ole-file entry) + (loop + for n from 1 + while t ;; TODO until eof! + do (multiple-value-bind (blib kind) + (read-record in dir n) + (format html "<p><img src=\"_~d.~(~a~)\">~%" n kind))))))))))))) + +;;; MS-PPT PowerPoint (.ppt) Binary File Format + +(define-structure RecordHeader () + (%dummy1 ubyte) + (%dummy2 ubyte) + (recVer t :compute (logand #x0f %dummy1)) + (recInstance t :compute (logior (ash %dummy2 4) (ash %dummy1 -4))) + (recType ushort) + (recLen dword)) + +(define-structure CurrentUserAtom () + (size dword :always #x14) + (headerToken dword) + (offsetToCurrentEdit dword) + (lenUserName ushort) + (docFileVersion ushort) + (majorVersion ubyte) + (minorVersion ubyte) + (unused ushort) + (ansiUserName (achar lenUserName)) + (relVersion dword) + (unicodeUserName (wchar lenUserName))) + +;;; MS-ODRAW Office Drawing Binary File Format + +(defclass shorter-stream (trivial-gray-streams:fundamental-binary-input-stream + trivial-gray-streams:trivial-gray-stream-mixin) + ((wrap :initarg :wrap) + (size :initarg :size) + (offset :initform 0))) + +(defmethod trivial-gray-streams::stream-element-type ((stream shorter-stream)) + '(unsigned-byte 8)) + +(defmethod trivial-gray-streams:stream-file-position ((stream shorter-stream)) + (with-slots (offset) stream + offset)) + +(defmethod trivial-gray-streams:stream-read-byte ((stream shorter-stream)) + (with-slots (wrap size offset) stream + (cond + ((< offset size) + (incf offset) + (read-byte wrap)) + (t :eof)))) + +(defun call-with-shorter-stream (stream fn) + (with-open-stream (x stream) + (funcall fn x))) + +(defmacro with-shorter-stream ((var wrap size) &body body) + `(call-with-shorter-stream + (make-instance 'shorter-stream :wrap ,wrap :size ,size) + (lambda (,var) ,@body))) +(defun read-record (stream dir &optional n) ;; TODO remove dir and n + (let ((x (read-RecordHeader stream))) + (with-slots (recVer recInstance recType recLen) x + (flet ((blip (ext) + (with-shorter-stream (in stream (RecordHeader.recLen x)) + (list x + (read-value 'guid in) + (read-value 'ubyte in) + (with-open-file (out (format nil "~a/_~d.~a" dir n ext) + :direction :output + :if-does-not-exist :create + :if-exists :supersede + :element-type '(unsigned-byte 8)) + (alexandria:copy-stream in out)))))) + (ecase recType + (#.RT_CurrentUserAtom + (assert (zerop recVer)) + (assert (zerop recInstance)) + (list x (read-CurrentUserAtom stream)) + #+nil + (with-shorter-stream (in stream (RecordHeader.recLen x)) + (list x (read-CurrentUserAtom in)))) ;; why recLen too small? + ((#xF01E) ;; OfficeArtBlipPNG + (assert (zerop recVer)) + (assert (member recInstance '(#x6e0 #x6e1))) + (values (blip "png") :png)) + (#xF01D ;; OfficeArtBlipJPEG + (assert (zerop recVer)) + (assert (member recInstance '(#x46A #x46B #x6E2 #x6E3))) + (values (blip "jpeg") :jpeg))))))) diff --git a/package.lisp b/package.lisp @@ -0,0 +1,2 @@ +(defpackage :ole + (:use :cl))