' RENPHOT(5.0)  Rename Digital Photo Image JPG Files   01/18/1999-09/28/2008
' --------------------------------------------------------------------------
' Copyright (C) 1999-2008 by Vladimir Veytsel                  www.davar.net

' Type ---------------------------------------------------------------------

'    Procedure

' Description --------------------------------------------------------------

'    Program generates unique names for image files recorded by a digital
'    camera basing on date-time stamps contained within files.  Generated
'    file names are unique only within the given year - it's not much of a
'    trouble to separate files between years, while this limitation permits
'    to maintain the most universal 8.3 naming standard.  Ideally a digital
'    camera itself should do this job, but file naming seems to be of a
'    little concern for camera designers.

'    Program reads all not entirely digitally named *.JPG files within the 
'    CURRENT directory and renames those that have recorded date and time
'    stamp (non-zero).  Recorded date and time can be adjusted by a fixed
'    value to handle the situation when pictures were taken in a time zone
'    different from one where camera clock was set, or to correct error of
'    the camera clock.  Along with file renaming program appends records 
'    to the DESCRIPT.ION file providing initial description approximations 
'    (with optional global comment) that can be adjusted manually later.

' Parameters (Up to 7 Positional Self-Delimited) ---------------------------

'    1. Global description comment to be added to every description line
'       (might be handy as an event or place identification that can be
'       later individually adjusted as necessary for selected images).

'    2. Date-time stamp delimiter recorded by the camera (Default: ":").

'    3. Number of date-time stamp entry to select (Default: 2).  Camera
'       usually writes several dates (Olympus writes 3, for example) and
'       certain investigation and trial might be required to determine what
'       date-time stamp is the most reliable (for Olympus D400 it happens
'       to be the 2-nd date).

'    4. Name of the last file from the previous memory cartridge in the form
'       of an eight-digit number "MODAHRUT" (Default or invalid: none).
'       This parameter might prove handy for downloading and renaming images
'       from several successive memory cartridges in case of a high frequency
'       of shots made at the end of one cartridge that results in the
'       incremental shift of file name numbers, so that this shift carries
'       over into the next cartridge.  This might happen because it's
'       necessary to download separate cartridges into separate directories
'       - camera provides unique names only within its current cartridge.
'       There's always a choice, of course, not to use this parameter, but
'       to do necessary image file name adjustments manually when merging
'       individual cartridge directories together.

'    5. Date translation pattern (Default: "MO/DA/CNYE")
'       Date is recorded by the camera as "CNYE:MO:DA" (":" stands here
'       for the camera-recorded date-time stamp delimiter), where:
'          CN  - Current century
'          YE  - Year  of current century
'          MO  - Month of current year
'          DA  - Day   of current month
'       Pattern for the date to be written to the corresponding line of
'       DESCRIPT.ION file can be composed of above listed date fields
'       and any delimiters different from DAMOYECN characters, e.g.:
'       MO/DA/CNYE  or  MO/DA/YE
'       DA.MO.CNYE  or  DA.MO.YE
'       CNYE-MO-DA  or  YE-MO-DA, etc.

'    6. Time translation pattern (Default: "HR:UT")
'       Time is recorded by the camera as "HR:UT:SZ" (":" stands here
'       for the camera-recorded date-time stamp delimiter), where:
'          HR  - Hour   of current day
'          UT  - Minute of current hour
'          SZ  - Second of current minute
'       Pattern for the time to be written to the corresponding line of
'       DESCRIPT.ION file can be composed of above listed time fields
'       and any delimiters different from HRUTSZ characters, e.g.:
'       HR:UT:SZ  or  HR:UT
'       HR.UT.SZ  or  HR.UT
'       HR-UT/SZ  or  HR-UT, etc.

'    7. Time adjustment increment/decrement (Default: none), e.g.:
'       +08:00  or   -02:30  - Should have fixed sHR:UT format, when specified.
'       HR&lt;=12  and  UT&lt;=59  - Invalid parameter (format or value) is ignored.
'       Time adjustment comes handy in case you've been travelling, and taking 
'       pictures in another time zone, but didn't adjust the camera clock 
'       (who does?).  This might be a minor annoyance, but why not to fix it
'       in the same run with image files' renaming?  
'       It's sufficient, of course, to have only hours for time zone adjustment;
'       minutes are added just in case it would be desirable to correct an error
'       of the camera clock.
'       Be careful with the sign setting; you've got to have it "+", if 
'       pictures were taken East of the location where camera clock was set
'       (things happen "earlier" there), and "-", if pictures were taken West. 

'    Note: In order to permit any symbols within global description comment,
'          and to accommodate for any possible recorded date-time stamp
'          delimiter, and any delimiters within date and time patterns,
'          parameter string is treated as being self-delimited, i.e. its
'          FIRST symbol is used as the delimiter of individual parameters.
'          It is up to the user to specify delimiter with the parameters
'          and to choose it carefully to avoid parameter misinterpretation.

'          RENPHOT                              - All parameters defaulted
'          RENPHOT _Comment, etc.               - Symbol "_" is the delimiter
'          RENPHOT /Comment, etc./:             - Symbol "/" is the delimiter
'          RENPHOT \Comment, etc.\:\1           - Symbol "\" is the delimiter
'          RENPHOT |Comment, etc.|:|3|04121443  - Symbol "|" is the delimiter

' Action -------------------------------------------------------------------

'  - Reads date and time stamp from every *.JPG file in the CURRENT directory
'    in the form of CNYE:MO:DA HR:UT:SZ (":" is assumed to be date delimiter).

'  - Renames each file into MODAHRUT.JPG using data from date and time stamp.
'                           +>+>+>+>
'                           | | | v
'                           | | v Minute (can be incremented over 59 to 99)
'                           | v Hour (can be incremented over 23, if required)
'                           v Day
'                           Month

'  - In case such name already exists, name is incremented by "+1" until
'    proper vacant name slot is found (the ACTUAL time is placed in the
'    description record).  Such incrementing might move the minute field
'    over 59 to 99 and under very rare circumstances hour field over 23.
'    (It's not a big deal to keep file name always as a proper date, but
'    it might tend to propagate the shift beyond month and even beyond year,
'    which doesn't make any sense.)

'  - Image description record is appended to DESCRIPT.ION file for future
'    use as ACDSee and 4DOS description line.  Description record has the
'    following format (includes WeeK Day identification):
'    MODAHRUT.JPG WKD MO/DA/CNYE HR:UT  [&lt;global_description_comment>]

'    Note:  Date and time are translated according to the default pattern
'           in the above example.  Alternative translation patterns can be
'           specified.

'  - Version 5.0 was modified (2008) to write 3 auxiliary files in addition
'    to renaming image files and writing DESCRIPT.ION file for the ACDSeee.
'
'    First two files permit an easy transition from 8.3 names to LFNs,
'    if LFNs are preferable to 8.3 image file names.
'    (NB: Renaming batch is supposed to be run in the CURRENT directory).

'    REN_LFN.BAT   - Batch file to rename each RENPHOT 8.3 renamed file into
'                    Long File Name file (run it in Windows Prompt, not DOS).
'                    Sample REN_LFN.BAT record:
'                    REN 08081221.JPG 2008-08-08_12-21-13.JPG

'    DESCRIPT.LFN  - DESCRIPT.ION for Long File Names
'                    Delete original 8.3-based DESCRIPT.ION and rename LFN-based
'                    DESCRIPT.LFN into DESCRIPT.ION for ACDSee accessibility.
'                    Sample DESCRIPT.LFN record:
'                    2008-08-08_12-21-13.JPG Fri 08/08/2008 12:21  Comment for LFN

'    Third file is a model for LFN to 8.3 renaming batch for rather rare case
'    of merging two image directories with close date-time stamps and multiple
'    shots take within same minutes (multiple cameras taking shots during same
'    period of time).

'    Image directory merging involves the following steps:

'    - Download images from each camera into a separate directory.
'    - Leave original camera image names in the first directory as-is.
'      E.g.: IMG_4251.JPG
'    - Globally rename original camera image names in the second directory.
'      E.g.: IMG_3768.JPG --> ABC_3768.JPG
'      Use command "REN IMG*.* ABC*.*" with "ABC" as a new file name prefix.
'    - Repeat previous step for each subsequent directory to be merged, choosing
'      choosing a unique file name prefix for each source directory files.
'    - Move files from all directories into the first one - to become merged dir
'      (no naming conflicts could occur here due to differences in prefixes).
'    - Run RENPHOT (without parameters) in the merged directory.
'    - Run REN_LFN.BAT in the merged directory (in Windows Prompt, not DOS).
'      Name conflicts (shots taken within the same second) are very unlikely;
'      if some happen, names should be adjusted manually using any file manager,
'      and corresponding changes should be made to LFN[s] in REN_8-3.BAT file.
'    - Adjust merged REN_8-3.BAT file as described below.
'    - Run REN_8-N.BAT in the merged directory (in Windows Prompt, not DOS).
'    - Delete *.BAT and DESCRIPT.* files in the merged directory.
'    - Run RENPHOT (with proper global comment and any other parameters that
'      might be necessary) in the merged directory - resultant file names will
'      reflect chronologically correct shooting sequence.

'    NB: For proper sequencing of files in the merges directory it is essential
'        that camera clocks are synchronized BEFORE collaborative shooting.  If
'        this wasn't done in the due time, necessary time adjustments could be
'        made using RENPHOT time adjustment parameter (see description above).
'        In this it would be necessary to run RENPHOT (with time adjustment an
'        commenting in each individual directory, and then proceed with manual
'        merging of REN_8-N.BAT and DESCRIPT.ION files.

'    REN_8-3.BAT   - Batch file model for renaming LFNs into properly ordered
'                    not fully digital (processable by RENPHOT) 8.3 names.
'                    (adjust it with text editor having good block manipulation
'                    capabilities, and run it in Windows Prompt, not DOS).
'                    Sample REN_LFN.BAT record:
'                    REN 2008-08-08_12-21-13.JPG I0000050.JPG

'    Adjustment of REN_8-3.BAT should be done as follows (QuickEdit or
'    TextPad in block mode can handle this easily):

'    - Cut 3-rd column (8.3 file name) and place it BELOW main text body.
'    - Sort main text body by 2-nd column (LFN name - full date-time stamp).
'    - Return 8.3 file name column starting at the top of main text body.
'    - Remove empty lines at the end of the file.

'    Note:  An answer to an obvious question "Why bother at all with all this?"
'           Indeed, using a simple mechanism of switching from 8.3 file names
'           to LFNs resolves the merging problem nicely just by eliminating it.

'           I have several reasons to stick to the 8.3 naming model:

'           The first reason is purely personal - a sheer bulk of accumulated
'           and properly commented photo archive makes global move to LFNs a
'           huge task - RENPHOT mechanism of global commenting can handle this
'           OK, but individual (manual) comment adjustment would be hard to
'           recover.  Whenever this is necessary there's an easy way to switch
'           from 8.3 names LFNs (full LFNs) - see DSC2REN that handles the
'           task basin only on DESCRIPT.ION file data.

'           Another reason is objective - most of DVD/DIVX players that are
'           quite handy for browsing through photos and slide shows on big
'           TV screen still display only (as of 2008) 8.3 file names.  Thus
'           having meaningful 8.3 file names is far more preferable for
'           navigation than otherwise auto-truncated to 8.3 LFNs.

'           And finally, in terms of maintaining photo archive as as I do it
'           (chronologically with major separation of years), LFNs have indeed
'           absolutely no advantage over 8.3 file names in combination with
'           DESCRIPT.ION file that I like anyway for it's simplicity and
'           convenience.  For some rare exceptions DSC2REN mentioned above
'           stands nicely to the task.

' Notes --------------------------------------------------------------------

'  - Program is designed to operate within CURRENT directory.

'  - Important presumption is that file names written by the camera are not
'    entirely digital (E.g.: they start with "P" for Olympus).

'  - If file name is entirely digital (except for ".JPG" extension) no
'    action is taken for such file.  This ensures that partially processed
'    directories can be entirely reprocessed safely to rename only what is
'    required.

'  - Program relies on the metadata with the date-time stamp that gets
'    written at the beginning of the *.JPG image file by a digital camera.
'    If date and time stamp is not found or it is all zeros, no action is
'    taken for such file (camera should be set to write date and time to
'    image file). To check the presence of metadata and date-time stamp
'    view *.JPG file as text.

'  - Renaming of image files should be done right after unloading them from
'    the camera to the computer.  If you plan to use RENPHOT, it should be
'    the very FIRST processing step.  Image processing programs such as 
'    ACDSee or PhotoShop (their early versions) could remove camera-recorded
'    metadata (they give warning, however), and date-time stamp gets lost,
'    making operation of RENPHOT impossible (running it anyway won't spoil
'    anything - files simply won't be renamed).

'  - Beginning of the date-time stamp is identified by the program as four
'    successive digits starting with "19" or "20" followed by the date
'    delimiter, E.g.:  "19yy:" or "20yy:".

'  - Program looks for date-time stamp only within first 1000 symbols of
'    *.JPG file.  If the number of date-time entry specified by the 3-rd
'    parameter is not found, file is not renamed.

' External SubProgram Library ----------------------------------------------

     $LINK "MODULE.PBL"

' External Function --------------------------------------------------------

     DECLARE FUNCTION DAY2GRE$(Day.Numb&amp;)
     DECLARE FUNCTION DIGITAL%(Strng$,Delim$)
     DECLARE FUNCTION GRE2DAY&amp;(Greg.Date$)
     DECLARE FUNCTION PARSE%  (Strng$,SubStr$(),Delim$)
     DECLARE FUNCTION TRANS$  (Strng$,Source$,Target$)
     DECLARE FUNCTION WEEKDAY$(Spec.Date$)

' Start Procedure ----------------------------------------------------------

     DEFINT A-Z     ' All defaulted variables are integer
     OPTION BASE 1  ' Default array indexation starts from "1"

' Constant -----------------------------------------------------------------

     Q$=CHR$(34)  ' Quotation mark

' Working Variable ---------------------------------------------------------

     DIM Parameter$(7)

' Get Control Parameters ---------------------------------------------------

     Parm$=COMMAND$
'    Parm$=""
'    Parm$="_Comment 1, etc."
'    Parm$="/Comment 2, etc./:"
'    Parm$="\Comment 3, etc.\:\1"
'    Parm$="|Comment 4, etc.|:|3|04121443"
'    Parm$="+Comment 5, etc.+:+3+04121443+CNYE-MO-DA"
'    Parm$="-Comment 6, etc.-:-3-04121443-DA.MO.CNYE-HR.UT.SZ"
'    Parm$="*Comment 7, etc.*:*3*04121443"
'    Parm$="/Comment 8, etc.//////+10:10"
'    Parm$="/Comment 9, etc.//////-01:01"
'    Parm$="/Comment for LFN"

     I=PARSE%(Parm$,Parameter$(),"*")

' Form Parameters' Actual Values -------------------------------------------

     Descr$   =Parameter$(1)
     Delim$   =Parameter$(2)
     Entry=VAL(Parameter$(3))
     Prev$    =Parameter$(4)
     Date.Pat$=Parameter$(5)
     Time.Pat$=Parameter$(6)
     Time.Adj$=Parameter$(7)

' Display Renaming Log Title -----------------------------------------------

     PRINT "RENPHOT(5.0)  Rename Digital Photo Image JPG Files  ";DATE$;
     PRINT "  ";LEFT$(TIME$, 5)
     PRINT STRING$(69,"-")
     
' Adjust Control Parameters ------------------------------------------------

     IF (Delim$="") THEN Delim$=":" :Delim$=LEFT$(Delim$,1)
     IF (Entry =0 ) THEN Entry =2

     IF (Date.Pat$="") THEN Date.Pat$="MO/DA/CNYE"
     IF (Time.Pat$="") THEN Time.Pat$="HR:UT"

     IF ((LEN(Prev$)&lt;>8)OR _
         (NOT(DIGITAL(Prev$,"")))) THEN
        Prev$=""
     END IF

     Rec.Dt.Tm.Pat$="CNYE"+Delim$+"MO"+Delim$+"DA HR"+Delim$+"UT"+Delim$+"SZ"

     IF (LEN(Time.Adj$)>0) THEN
        Sign.Adj$=LEFT$(Time.Adj$,1)
        IF ((LEN(Time.Adj$)=6)        AND _  ' ----+-
            (VERIFY(Sign.Adj$,"-+")=0)AND _  ' +12:34
            (MID$(Time.Adj$,4,1)=":") AND _
            (DIGITAL(MID$(Time.Adj$,2,2)+RIGHT$(Time.Adj$,2),""))) THEN
           Hours.Adj  =VAL(  MID$(Time.Adj$,2,2))
           Minutes.Adj=VAL(RIGHT$(Time.Adj$,2))
           IF ((Hours.Adj&lt;=12)AND _
               (Minutes.Adj&lt;=59)) THEN
              Minutes.Incr=Hours.Adj*60+Minutes.Adj
              IF (Sign.Adj$="-") THEN
                 Minutes.Incr=Minutes.Incr*(-1)
              END IF
              PRINT "Note: Time of each picture will be adjusted by the value of ";Q$;Time.Adj$;Q$
           ELSE
              PRINT "Invalid time adjustment parameter value[s]: ";Q$;Time.Adj$;Q$;"  - Ignored"
           END IF
        ELSE
           PRINT "Invalid time adjustment parameter format: ";Q$;Time.Adj$;Q$;"  - Ignored"
        END IF
     END IF

' Open Directory Description File ------------------------------------------

     OPEN "DESCRIPT.ION" FOR APPEND AS #1
     OPEN "REN_LFN.BAT"  FOR APPEND AS #3
     OPEN "DESCRIPT.LFN" FOR APPEND AS #4
     OPEN "REN_8-3.BAT"  FOR APPEND AS #5

' Process All Files in Current Directory (Having Non-Digital Name) ---------

     ON ERROR RESUME NEXT      ' Bypass system action on file renaming error

     File.Name$=DIR$("*.JPG")  ' Get first file name

     DO UNTIL (File.Name$="")
        IF (NOT(DIGITAL(LEFT$(File.Name$,8),""))) THEN
           GOSUB Process.File
        END IF
        File.Name$=DIR$        ' Get next  file name
     LOOP

' Finish Program -----------------------------------------------------------

     PRINT "********"
     CLOSE
     END

Process.File:  ' Routine ---------------------------------------------------

     GOSUB Get.Date.Time

     IF ((LEN(Date.Time$)>0)AND _  ' File contains date and time stamp
         (DIGITAL(Date.Time$," "+Delim$))) THEN
        GOSUB Form.New.Name
        IF ((New.Name$&lt;>"00000000")AND _
            (File.Name$&lt;>New.Name$+".JPG")) THEN
           GOSUB Rename.File
           GOSUB Append.Descr
        END IF
     END IF

     RETURN  ' From Process.File routine

Get.Date.Time:  ' Routine --------------------------------------------------

     OPEN File.Name$ FOR BINARY AS #2

     I&amp;=1
     FOR J=1 TO Entry
         Year$="****"
         FOR I&amp;=I&amp; TO 1000    ' Find the nearest date delimiter
             GET$ #2,1,Symb$  ' which follows 4 digits (19yy or 20yy)
             Year$=RIGHT$(Year$+Symb$,5)
             IF ((Symb$=Delim$)         AND _
                 (DIGITAL(Year$,Delim$))AND _
                 (( LEFT$(Year$,2)="19")OR  _
                  ( LEFT$(Year$,2)="20"))) THEN
                EXIT
             END IF
         NEXT I&amp;
         IF (J&lt;>Entry) THEN
            I&amp;=I&amp;+15
            SEEK #2,I&amp;  ' Skip date and time stamp
         END IF
     NEXT J

     SEEK #2,I&amp;-4+(Entry=1)
     GET$ #2,19,Date.Time$

     CLOSE #2

'    ----+----1----+----  Time adjustment is below
'    2002:04:21 10:10:36  Date.Time$ format example
     
     File.Date.Time$=Date.Time$  ' Save for displaying in renaming log
     Long.File.Name$=TRANS$(TRANS$(File.Date.Time$,Delim$,"-")," ","_")

     IF (Minutes.Incr&lt;>0) THEN
        Minutes.Abs&amp;=GRE2DAY&amp;(MID$(Date.Time$, 6,5)+":"   + _
                             LEFT$(Date.Time$,   4))*24*60+ _
                          VAL(MID$(Date.Time$,12,2))   *60+ _
                          VAL(MID$(Date.Time$,15,2)) 
        
        Minutes.Abs&amp;=Minutes.Abs&amp;+Minutes.Incr
        
        Hours.Abs&amp;=INT(Minutes.Abs&amp;/60)
        MID$(Date.Time$,15,2)=RIGHT$("0"+LTRIM$(STR$(Minutes.Abs&amp; MOD 60)),2)
        Days.Abs&amp;=INT(Hours.Abs&amp;/24)
      MID$(Date.Time$,12,2)=RIGHT$("0"+LTRIM$(STR$(Hours.Abs&amp; MOD 24)),2)
        Adjusted.Date$=DAY2GRE$(Days.Abs&amp;)
        MID$(Date.Time$,1,10)=RIGHT$(Adjusted.Date$,4)+":"+LEFT$(Adjusted.Date$,5)
        MID$(Date.Time$,8,1)=":"  ' Adjust delimiter
     END IF

     RETURN  ' From Get.Date.Time routine

Form.New.Name:  ' Routine --------------------------------------------------

     New.Name$=TRANS$("MODAHRUT",Rec.Dt.Tm.Pat$,Date.Time$)

     IF (New.Name$=Prev$) THEN  ' Increment name number to avoid duplicate
         New.Name$=RIGHT$("0"+LTRIM$(STR$(VAL(New.Name$)+1)),8)
     END IF

     RETURN  ' From Form.New.Name routine

Rename.File:  ' Routine ----------------------------------------------------

     Renamed=0
     DO UNTIL (Renamed)
        NAME File.Name$ AS New.Name$+".JPG"
        IF (ERRTEST=58) THEN  ' New.Name$ already exists
           New.Name$=RIGHT$("0"+LTRIM$(STR$(VAL(New.Name$)+1)),8)
        ELSE
           PRINT File.Name$;" --> ";New.Name$;".JPG  ";File.Date.Time$
           Renamed=-1
           PRINT #3,"REN ";New.Name$;".JPG ";Long.File.Name$;".JPG"
           File.Counter=File.Counter+1
           File.Number$=RIGHT$("00000"+LTRIM$(STR$(File.Counter)),6)
           PRINT #5,"REN ";Long.File.Name$;".JPG I";File.Number$;"0.JPG"
        END IF
     LOOP

     RETURN  ' From Rename.File routine

Append.Descr:  ' Routine ---------------------------------------------------

     PRINT #1,New.Name$;".JPG "                                        ; _
              WEEKDAY(TRANS$("MO-DA-YE",Rec.Dt.Tm.Pat$,Date.Time$));" "; _
                      TRANS$(Date.Pat$ ,Rec.Dt.Tm.Pat$,Date.Time$) ;" "; _
                      TRANS$(Time.Pat$ ,Rec.Dt.Tm.Pat$,Date.Time$);"  "; _
                      Descr$

     PRINT #4,Long.File.Name$;".JPG "                                  ; _
              WEEKDAY(TRANS$("MO-DA-YE",Rec.Dt.Tm.Pat$,Date.Time$));" "; _
                      TRANS$(Date.Pat$ ,Rec.Dt.Tm.Pat$,Date.Time$) ;" "; _
                      TRANS$(Time.Pat$ ,Rec.Dt.Tm.Pat$,Date.Time$);"  "; _
                      Descr$

     RETURN  ' From Append.Descr routine
