Monday, October 18, 2010

LootGrab: HTML5 Game from Triangle Game Jam 2010


Spoilers in the Video! Consider Playing LootGrab first. (As of Sept 2010, Chrome was definitely the best option since Firefox and IE struggled, you can give your smart phone a try too).

2010 Triangle Game Jam game: LootGrab Video on youtube, or LootGrab Video on vimeo.

This year brought changes from the Game Jams of past:
First, I moved from the Research Triangle and now work at Google, and Adrienne came too (we've worked together on 5 of the game jam projects now). So, we got some fresh blood at Google to join us and ran a game jam in parallel with the 2010 Triangle Game Jam.

Second, instead of using C# we used HTML5 this year:

Third, you can Play LootGrab with a click of a button - ridiculously easy compared to all previous Jams where I didn't even bother giving you the gazillion prerequisites required.

Theme and Game Ideas
The theme this year was, "Placing Blocks". Here is my game concept, which didn't make the cut::


(someone pointed out it would be great from the side too, with ballistic arcs.)

We voted up ideas, and Adrienne's one out: LootGrab is about placing down blocks in a dungeon to influence the hero, instead of controlling the hero directly. The greedy guy runs for the closest loot, food, or exit ... without care for monsters or traps.

We figured we'd need a map editor, the runtime, and perhaps a level sharing system online via AppEngine. I was particularly attached to an idea of allowing user contributed game object definitions. Allow a user to upload an image and a snippit of javascript that defines it's behavior. How cool would a mod-able game jam game be? :) That was stretching a bit far though.

HTML5

HTML5 is a grab bag of new functionality in browsers. Some of it is pretty cool (peer to peer networking, local storage, video and audio tags). We focused on two simple components, canvas 2d to draw and audio for sound effects.

In my day job I'm working to accelerate canvas with GPUs, as are others at Microsoft, Mozilla, and Apple. It's fairly fast even in software, and LootGrab runs fine without GPU acceleration. In fact, it runs on phones pretty well, such as my Nexus One Android phone. That's pretty cool, all we did to support mobile was to make sure we handled low frame rates without changing gameplay. To do that we used fixed time step gameplay logic (tick based), and just run as many ticks as needed to cover the amount of time elapsed.

Our use of canvas is basically clearing it, drawing a pile of sprites (via sub-rectangles of larger images), and also a line to show where the player is moving. Actually, we have a few layers of canvas stacked on top of each other. Theoretically we could have saved performance by not redrawing non animating tiles - just compositing them underneath.

Adrienne took on audio for sound effects, and did run into a bit of trouble. The sound effects were very short, and had to be padded out to longer audio lengths to trigger properly. Also, multiple instances needed to be created in case the sound was played more than once.

Javascript

Several of us hadn't done anything substantial in Javascript before. Certainly not an object oriented game entity system that can factory from user created levels. Some complicated flurry of activity by Glen, Ian, Nat, and Gregg made that happen. The result could be cleaner, but worked well. We have JSON data blobs, e.g. for the tile definitions.

Things I loved:
Need to add extra data to your level components of game object definitons? Perhaps only to particular items? No problem! Just start typing. At runtime it is trivial to just check if the data is there and use it if so.

Writing some code and wish you could hang more data off an object? Just set that value! Check to see if it's === "undefined" later and you can pick up  your special data easily. Object definitions don't have to worry about implementation details of other systems, and those other systems don't need extra book keeping kept in parallel. e.g.:

  try {
    ctx.drawImage(this.img, ...);
  } catch(e) {
    if(this.error_printed === undefined) {
      tdl.log("problem with image " + this.entDefID);
      this.error_printed = true;
  };


Development tools: Logging. Resource load timeline. Immediate mode editor: Hit a breakpoint, and just execute some code at the Javascript console.

Fast iteration time, though C# was great for that too.

Instant continuous "build"! Glen installed an Auto Reload Chrome extension and put the game up on a projector. Check in some code and see the game running it in 20 seconds. ;) Helps to have a game that can play its self.

Libraires such as TDL, and JQuery: some helper code for Javascript. It's not so important what you use, but you definitely want to not worry about the minutia.

Not so great?
I didn't use an IDE that had code analysis, and that's a very convenient feature of MSVC. Though, Ian had good things to say about WebStorm.

Also "classes" in javascript are syntactically very sad, and inheritance to my novice eyes looks messy. And variable "scoping" is dicey.

Debugging is functional and GUI, which is better than what most programmers use around my on linux. But it falls short of a modern debugger such as MSVC with C++ or C#.

Also, deciphering a web page via HTML, script, HTML embedded in script, CSS files, and dynamic changes to styles? ... yikes.

Things for Next Time


Would be nice to have some basics already written:
- Factory that will created entities from JSON data packs
- Cleaner audio solution
- Sprite system for canvas

Smaller teams. We had six on this project, and that's a bit much for a game jam game. Several were first time jammers, and several Javascript newbies, so it did really help to share know-how. But we wasted a lot of time getting started, coming to consensus on implementation choices, and stepping on each other's code.

The End

And now I leave you with some screen shots:


And a thanks to whoever oryx is, who created the sprites we used:

Monday, October 4, 2010

Scriptcode: misc batch files and visual studio macros

Here are the random macros I use in Visual Studio and windows batch files. Nothing monumental, but I find them useful often enough.
  • scheib.vb
    • The general purpose Visual Studio macros I use, particularly useful to me are:
  • addpath.bat
    • Eases adding more directories to your path environment variable.
  • cmd_here.bat
    • Right click any directory or file in windows explorer or a file save/open dialog and get a command prompt at that location.
  • copy-certain-files.pl
    • Assists automation to copy certain files from one directory to another, e.g. just the .html files but not the images.
  • remove_empty_directories.bat
    • Cleans up a directory tree to not have empty directories.
The following are useful to have when writing a batch file:
  • isadirectory.bat
  • isafile.bat
  • isemptydirectory.bat
Files can be downloaded here:
http://gist.github.com/582050 - visual studio macros
http://gist.github.com/582036 - batch files



Imports EnvDTE
Imports System
Imports System.Diagnostics
Imports System.Windows.Forms
Imports System.Collections.Generic
'------------------------------------------------------------------------------
'FILE DESCRIPTION: scheib.vb Vincent Scheib's macros
'------------------------------------------------------------------------------
Public Module scheib
'_______________________________________________________________________________
Sub InsertCommentBars()
'DESCRIPTION: Creates a comment block
Dim Descr As String
Descr = "//---------------------------------------------------------------------------" + vbLf
ActiveDocument.Selection().text = Descr
End Sub
'_______________________________________________________________________________
Sub InsertCommentPrefix(ByVal StrPrefix)
'DESCRIPTION: Inserts this text at the cursor, or if there is a selection, prefixes lines with it.
'By Vincent Scheib
Dim win
win = ActiveWindow
If win.Kind <> "Document" Then
MsgBox("This macro can only be run when a text editor window is active.")
Else
If Len(ActiveDocument.Selection().text) = 0 Then
'Insert the text here
ActiveDocument.Selection().text = StrPrefix
Else
'Prefix lines with text
Dim StartLine = ActiveDocument.Selection.TopPoint.Line
Dim EndLine = ActiveDocument.Selection.BottomPoint.Line
Dim i
For i = StartLine To EndLine
ActiveDocument.Selection.GoToLine(i)
ActiveDocument.Selection().text = StrPrefix + ActiveDocument.Selection().text
Next
End If
End If
End Sub
'_______________________________________________________________________________
Sub InsertCommentVES()
InsertCommentPrefix("//VES: ")
End Sub
'_______________________________________________________________________________
Sub InsertCommentX()
InsertCommentPrefix("//VES:X ")
End Sub
'_______________________________________________________________________________
Sub InsertCommentBang()
InsertCommentPrefix("//VES:! ")
End Sub
'_______________________________________________________________________________
Sub InsertCommentDebug()
InsertCommentPrefix("//VES:DEBUG ")
End Sub
'_______________________________________________________________________________
Sub InsertCommentTodo()
InsertCommentPrefix("//TODO vscheib ")
End Sub
'_______________________________________________________________________________
Sub InsertDate()
Dim ThisMonth As String
Dim ThisDate As String
Select Case Month(Now())
Case 1 : ThisMonth = "January"
Case 2 : ThisMonth = "February"
Case 3 : ThisMonth = "March"
Case 4 : ThisMonth = "April"
Case 5 : ThisMonth = "May"
Case 6 : ThisMonth = "June"
Case 7 : ThisMonth = "July"
Case 8 : ThisMonth = "August"
Case 9 : ThisMonth = "September"
Case 10 : ThisMonth = "October"
Case 11 : ThisMonth = "November"
Case 12 : ThisMonth = "December"
End Select
ThisDate = ThisMonth + " " & Microsoft.VisualBasic.DateAndTime.Day(Now) & " " & Year(Now())
ActiveDocument.Selection().text = ThisDate
End Sub
'_______________________________________________________________________________
Sub FindActiveFileInSolution()
'DESCRIPTION: Highlights the currently active document in the solution explorer (toggles file tracking on then off)
'Get a reference to the Command window.
Dim win As EnvDTE.Window = DTE.Windows.Item(EnvDTE.Constants.vsWindowKindCommandWindow)
Dim CW As EnvDTE.CommandWindow = win.Object
Dim TheStatusBar As EnvDTE.StatusBar = DTE.StatusBar
Try
'Input a command into the Command window and execute it.
CW.SendInput("View.TrackActivityinSolutionExplorer true", True)
CW.SendInput("View.TrackActivityinSolutionExplorer false", True)
TheStatusBar.Text = "Found."
Catch
TheStatusBar.Text = "Failed. Check that a document active and selected..."
TheStatusBar.Highlight(True)
End Try
End Sub
'_______________________________________________________________________________
'Sub FindNextLongLine(ByVal bStartAtTop As Boolean = False)
Sub FindNextLongLine(Optional ByVal bStartAtTop As Boolean = False)
'DESCRIPTION: Finds next line down from cursor that is too long.
'By Vincent Scheib
'CONFIGURE: set the maximum line length
Dim iMaxLength = 79
Dim win
win = ActiveWindow
If win.Kind <> "Document" Then
MsgBox("This macro can only be run when a text editor window is active.")
Else
' Setup status bar
Dim TheStatusBar As EnvDTE.StatusBar = DTE.StatusBar
Dim StartLine = ActiveDocument.Selection.TopPoint.Line
Dim iLine
If bStartAtTop Then
iLine = 1
Else
iLine = StartLine
End If
Do
Try ' Try moving to next line
ActiveDocument.Selection.GoToLine(iLine)
Catch
' We have reached end of document
ActiveDocument.Selection.GoToLine(StartLine)
TheStatusBar.Text = "Did not find a line too long."
TheStatusBar.Highlight(True)
Exit Do
End Try
Dim length = ActiveDocument.Selection.TopPoint.LineLength
If (length > iMaxLength) Then
ActiveDocument.Selection.SelectLine()
TheStatusBar.Text = "Line " + iLine.ToString() + " length: " + length.ToString()
Exit Do
End If
iLine = iLine + 1
Loop
End If
End Sub
'_______________________________________________________________________________
Sub FindFirstLongLine()
'DESCRIPTION: Finds first line in a document that is too long.
'By Vincent Scheib
FindNextLongLine(True)
End Sub
'_______________________________________________________________________________
Sub ShowDebugWindows()
'DESCRIPTION: Opens commonly used debug windows
DTE.ExecuteCommand("Debug.Watch")
DTE.ExecuteCommand("Debug.Locals")
DTE.ExecuteCommand("Debug.Autos")
DTE.ExecuteCommand("Debug.Threads")
DTE.ExecuteCommand("View.Output")
End Sub
'_______________________________________________________________________________
Dim ClipboardString As String
Sub CopyFilenameToClipboard()
'DESCRIPTION: Copies the selected solution item or active file's pathname to the windows clipboard
'By Vincent Scheib
Dim names As List(Of String) = GetSelectedSolutionItemFilenames()
If (names.Count >= 1) Then
ClipboardString = names(0)
Else
ClipboardString = ActiveDocument.FullName
End If
Dim ClipBoardThread As System.Threading.Thread = New System.Threading.Thread(AddressOf _CopyToClipboard_ThreadProcedure)
With ClipBoardThread
.ApartmentState = System.Threading.ApartmentState.STA
.IsBackground = True
.Start()
'-- Wait for copy to happen
.Join()
End With
ClipBoardThread = Nothing
' Setup status bar
Dim TheStatusBar As EnvDTE.StatusBar = DTE.StatusBar
TheStatusBar.Text = "Copied active document filename to clipboard."
TheStatusBar.Highlight(True)
End Sub
Sub _CopyToClipboard_ThreadProcedure()
System.Windows.Forms.Clipboard.SetDataObject(ClipboardString, True)
End Sub
Sub P4add()
'DESCRIPTION: Adds active document or selected solution items to perforce
For Each name As String In GetSelectedSolutionItemFilenames()
Shell("cmd /c (p4 add """ + name + """ ) & (pause)", AppWinStyle.NormalFocus)
Next
End Sub
Sub P4revert()
'DESCRIPTION: Reverts active document or selected solution items in perforce
For Each name As String In GetSelectedSolutionItemFilenames()
Shell("cmd /c (p4 revert """ + name + """ ) & (pause)", AppWinStyle.NormalFocus)
Next
End Sub
Sub P4delete()
'DESCRIPTION: Deletes active document or selected solution items in perforce
For Each name As String In GetSelectedSolutionItemFilenames()
Shell("cmd /c (p4 delete """ + name + """ ) & (pause)", AppWinStyle.NormalFocus)
Next
End Sub
Sub P4edit()
'DESCRIPTION: Opens active document or selected solution items for edit in perforce
For Each name As String In GetSelectedSolutionItemFilenames()
Shell("cmd /c (p4 edit """ + name + """ ) & (pause)", AppWinStyle.NormalFocus)
Next
End Sub
Sub P4diff()
'DESCRIPTION: Diffs active document or selected solution items in perforce
For Each name As String In GetSelectedSolutionItemFilenames()
Shell("cmd /c (p4 diff """ + name + """ ) & (pause)", AppWinStyle.NormalFocus)
Next
End Sub
Sub P4history()
'DESCRIPTION: Displays history of active document or selected solution items in perforce
For Each name As String In GetSelectedSolutionItemFilenames()
Shell("cmd /c (p4win -H """ + name + """ )", AppWinStyle.NormalFocus)
' pause not performed on this command because it will never return useful error text.
Next
End Sub
Function GetSelectedSolutionItemFilenames() As List(Of String)
Dim names As List(Of String) = New List(Of String)
For Each selectedItem As EnvDTE.SelectedItem In DTE.SelectedItems
Dim Found = False
Try ' to get project filename
If Not names.Contains(selectedItem.Project.FullName) Then
names.Add(selectedItem.Project.FullName)
End If
Found = True
Catch
Try ' to get project items filenames
For i As Short = 1 To selectedItem.ProjectItem.FileCount()
If (selectedItem.ProjectItem.FileNames(i).Length > 0) Then
If Not names.Contains(selectedItem.ProjectItem.FileNames(i)) Then
names.Add(selectedItem.ProjectItem.FileNames(i))
End If
Found = True
End If
Next i
Catch
End Try
End Try
If Not Found Then
' Determine if solution is selected and get filename.
If DTE.Solution.FullName.Contains(selectedItem.Name + ".sln") Then
If Not names.Contains(DTE.Solution.FullName) Then
names.Add(DTE.Solution.FullName)
End If
End If
End If
Next
If names.Count = 0 Then
MsgBox("No active document or selcted items. Try turning on the following option:" + vbLf + vbLf + "Tools->Options->Environment->Documents->Show Miscelaneous Files in Solution Explorer")
End If
Return names
End Function
'_______________________________________________________________________________
Sub HeaderFlip()
'DESCRIPTION: Flips between .h .cpp ... files
'By Vincent Scheib
'Searches open documents, the solution file list, and files on disk
'CONFIGURE: add extensions to flip between here, in the order to flip
Dim extensions() As String
Dim extensionsCpp() As String = {".h", ".inc", ".inl", ".hpp", ".cpp"}
Dim extensionsCs() As String = {".designer.cs", ".cs", ".resx"} ' prefer longer extention match
' Setup status bar
Dim TheStatusBar As EnvDTE.StatusBar = DTE.StatusBar
TheStatusBar.Text = "Searching for a header flip..."
Dim numExtensionsCpp = extensionsCpp.GetLength(0)
Dim numExtensionsCs = extensionsCs.GetLength(0)
Dim activeDoc = ActiveDocument.Name
Dim activePath = ActiveDocument.Path
' Determine current extension
Dim indexForActiveFileExtension As Integer = -1
' Check Cpp
For I As Integer = 0 To numExtensionsCpp - 1
Dim extension = extensionsCpp(I)
If InStr(activeDoc, extensionsCpp(I)) Then
indexForActiveFileExtension = I
extensions = extensionsCpp
Exit For
End If
Next
' Check Cs
For I As Integer = 0 To numExtensionsCs - 1
Dim extension = extensionsCs(I)
If InStr(activeDoc, extensionsCs(I)) Then
indexForActiveFileExtension = I
extensions = extensionsCs
Exit For
End If
Next
' Check for error
If indexForActiveFileExtension = -1 Then
TheStatusBar.Text = "Could not header flip: don't recognize active file's extension."
TheStatusBar.Highlight(True)
Return
End If
Dim numExtensions = extensions.GetLength(0)
Dim numExtensionsToTry = numExtensions - 1
Dim switchToDocs(numExtensionsToTry - 1) As String
' Populate list of filenames to switch to
Dim activeDocExtLen = Len(extensions(indexForActiveFileExtension))
Dim activeDocBase = Left(activeDoc, Len(activeDoc) - activeDocExtLen)
For I As Integer = 0 To numExtensionsToTry - 1
Dim extension = extensions((indexForActiveFileExtension + I + 1) Mod numExtensions)
switchToDocs(I) = activeDocBase + extension
Next
' Try the files:
For Each switchToDoc As String In switchToDocs
' Try to switch to already open file (with full path name match)
If (TrySwitchTo_OpenFile_FullName(activePath + switchToDoc)) Then
TheStatusBar.Text = ""
Return
' Try to open file from projects (with full path name match)
ElseIf (TrySwitchTo_ProjectFile(activePath + switchToDoc)) Then
TheStatusBar.Text = ""
Return
' Try to open file from disk from same path
ElseIf (TryOpen(activePath + switchToDoc)) Then
TheStatusBar.Text = ""
Return
' Try to open file from projects (any path)
ElseIf (TrySwitchTo_ProjectFile(switchToDoc)) Then
TheStatusBar.Text = ""
Return
' Try to switch to already open file (any path)
ElseIf (TrySwitchTo_OpenFile_Name(switchToDoc)) Then
TheStatusBar.Text = ""
Return
End If
Next
TheStatusBar.Text = "Failed to find any file to flip to."
TheStatusBar.Highlight(True)
End Sub
Sub SelectDependentProjects()
'DESCRIPTION: Step 1 of 2 for setting dependencies on projects
DependsHelp.SelectedProjects = GetSelectedProjects()
Dim OutputString As String
OutputString = DependsHelp.SelectedProjects.Count.ToString
OutputString += " Selected Projects:" + vbLf
OutputString += GetStringOfEachProject(DependsHelp.SelectedProjects)
MsgBox(OutputString)
End Sub
Sub SelectDependeeProjects_AssignDependencies()
'DESCRIPTION: Step 2 of 2 for setting dependencies on projects
If DependsHelp.SelectedProjects Is Nothing Then
MsgBox("You must first select projects to have dependencies set on, with SelectDependentProjects macro")
Return
End If
Dim DependentProjs As List(Of EnvDTE.Project) = DependsHelp.SelectedProjects
Dim DependeeProjs As List(Of EnvDTE.Project) = GetSelectedProjects()
Dim OutputString As String
OutputString = "Are you sure you want to set" + vbLf + vbLf
OutputString += DependentProjs.Count.ToString + " Projects:" + vbLf
OutputString += GetStringOfEachProject(DependentProjs) + vbLf + vbLf
OutputString += "As dependent upon" + vbLf + vbLf
OutputString += DependeeProjs.Count.ToString + " Projects:" + vbLf
OutputString += GetStringOfEachProject(DependeeProjs) + vbLf + vbLf
If MsgBox(OutputString, MsgBoxStyle.OkCancel) = MsgBoxResult.Cancel Then
Return
End If
For Each Dependent As EnvDTE.Project In DependentProjs
For Each Dependee As EnvDTE.Project In DependeeProjs
Try
DTE.Solution.SolutionBuild.BuildDependencies.Item(Dependent).AddProject(Dependee.UniqueName)
Catch ex As System.Exception
Dim Result As Microsoft.VisualBasic.MsgBoxResult
Result = MsgBox("Failed to add dependency: " + vbLf _
+ "Dependent: " + Dependent.Name + vbLf _
+ "on" + vbLf _
+ "Dependee: " + Dependee.Name + vbLf + vbLf _
+ "Error is:" + vbLf + ex.Message + vbLf + vbLf _
+ "CONTINUE????", MsgBoxStyle.YesNo)
If Result = MsgBoxResult.No Then
Return
End If
End Try
Next
Next
MsgBox("Done.")
End Sub
End Module
Module HeaderFlipHelp
'DESCRIPTION: Helper functions for HeaderFlip
'By Vincent Scheib
'_______________________________________________________________________________
Function TrySwitchTo_OpenFile_FullName(ByVal filename As String) As Boolean
For Each tryDocument As Document In DTE.Documents
Try
If tryDocument.FullName = filename Then
tryDocument.Activate()
Return True
End If
Catch
End Try
Next
Return False
End Function
'_______________________________________________________________________________
Function TrySwitchTo_OpenFile_Name(ByVal filename As String) As Boolean
For Each tryDocument As Document In DTE.Documents
Try
If tryDocument.Name = filename Then
tryDocument.Activate()
Return True
End If
Catch
End Try
Next
Return False
End Function
'_______________________________________________________________________________
Function TrySwitchTo_ProjectFile(ByVal filename As String) As Boolean
Try
Dim item As ProjectItem = DTE.Solution.FindProjectItem(filename)
item.Open()
item.Document.Activate()
Return True
Catch
End Try
Return False
End Function
'_______________________________________________________________________________
Function TryOpen(ByVal filename As String) As Boolean
Try
DTE.Documents.Open(filename, "Text")
Return True
Catch
Try
DTE.ItemOperations.OpenFile(filename)
Return True
Catch
End Try
End Try
Return False
End Function
End Module
Public Module DependsHelp
Public SelectedProjects As List(Of EnvDTE.Project)
Function GetSelectedProjects() As List(Of EnvDTE.Project)
Dim projs As List(Of EnvDTE.Project) = New List(Of EnvDTE.Project)
For Each selectedItem As EnvDTE.SelectedItem In DTE.SelectedItems
Try ' to get projects
If Not selectedItem.Project Is Nothing Then
If Not projs.Contains(selectedItem.Project) Then
projs.Add(selectedItem.Project)
End If
End If
Catch
End Try
Next
Return projs
End Function
Function GetStringOfEachProject(ByVal ProjectsList As List(Of EnvDTE.Project)) As String
Dim OutputString As String = ""
For Each proj As EnvDTE.Project In ProjectsList
If OutputString.Length > 0 Then ' add new line
OutputString += vbLf
End If
OutputString += " " + proj.Name
Next
Return OutputString
End Function
End Module
view raw scheib.vb hosted with ❤ by GitHub


@echo off
call :GOSUB__IS_A_DIR %1
if errorlevel 1 goto ERROR_not_found
set path_backup=%path%
set path=%1;%path%
echo Updated path. Backed up old path to path_backup.
goto END
:==ERROR_not_found
echo.
echo. Could not find directory:
echo. %1
echo.
echo. doing nothing.
echo.
goto END
:==GOSUB__IS_A_DIR
REM INPUT %1
REM OUTPUT errorlevel == 1 if input is not a dir
if (%1)==() exit /b 1
pushd "%~1" 2> nul
if errorlevel 1 exit /b 1
popd
exit /b 0
:END
view raw addpath.bat hosted with ❤ by GitHub
@echo off
:
: Place this file in your SendTo folder
: Right click a file or directory, and send to this .bat
: A cmd window will be opened in the directory of that file.
:
if (%1)==() goto ERROR
call :GOSUB__IS_A_DIR %1
if errorlevel 1 (
start cmd /K cd /D "%~d1%~p1"
) ELSE (
start cmd /K cd /D "%~f1"
)
goto END
:==ERROR
echo.
echo. Designed for a "Send To"
echo. expected an argument of a file or directory
goto END
:==GOSUB__IS_A_DIR
REM INPUT %1
REM OUTPUT errorlevel == 1 if input is not a dir
if (%1)==() exit /b 1
pushd "%~1" 2> nul
if errorlevel 1 exit /b 1
popd
exit /b 0
:END
view raw cmd_here.bat hosted with ❤ by GitHub
#use File::Copy;
print "Copies a list of files from a dir to another.\n";
print "\n";
print "Usage\n";
print " sourcefilelist.txt source_dir dest_dir\n";
print "\n";
print " where sourcefilelist.txt contains a relative filename per line.\n";
print "\n";
print " e.g. to copy c:\\test\\subdir\\file.txt to d:\\output\\subdir\\file.txt\n";
print " place 'subdir\\file.txt' into t.txt.\n";
print " call this script with arguments 't.txt c:\\test d:\\output'.\n";
print "\n";
$filelist_filename = $ARGV[0];
$srcdir_filename = $ARGV[1];
$dstdir_filename = $ARGV[2];
# Check input args
# does file list exist
open(filelist_file, "< $filelist_filename") or die "no file $filelist_filename : $!";
# does file source dir exist
opendir (srcdir_handle, "$srcdir_filename") or die "no dir $srcdir_filename : $!";
while( <filelist_file> )
{
chop;
$filetobecopied = "$srcdir_filename/$_";
$newfile = "$dstdir_filename/$_";
# flip all slashes to backslashes
$filetobecopied =~ tr|/|\\|;
$newfile =~ tr|/|\\|;
# remove the file name from dest file, just get the path
($newfilepath) = $newfile =~ m|(.*\\)|;
print "\n";
system("echo xcopy $filetobecopied $newfilepath");
system("xcopy $filetobecopied $newfilepath");
}
@echo off
if (%1)==() goto ERROR_USAGE
if not exist "%~1" exit /B 1
if not exist "%~1\" exit /B 1
exit /B 0
:== ERROR_USAGE
echo.
echo. %0
echo. usage:
echo.
echo. Call with a path name.
echo. If it is a directory, the ERRORLEVEL will be left 0
echo. If it is a file, ERRORLEVEL will be 1
echo. If it does not exist, ERRORLEVEL will be 1
pause
exit /B
@echo off
if (%1)==() goto ERROR_USAGE
if not exist "%~1" exit /B 1
if exist "%~1\" exit /B 1
exit /B 0
:== ERROR_USAGE
echo.
echo. %0
echo. usage:
echo.
echo. Call with a path name.
echo. If it is a file, ERRORLEVEL will be 0
echo. If it is a directory, the ERRORLEVEL will be 1
echo. If it does not exist, ERRORLEVEL will be 1
pause
exit /B
view raw isafile.bat hosted with ❤ by GitHub
@echo off
if (%1)==() goto ERROR_USAGE
call isadirectory.bat %1
if ERRORLEVEL 1 exit /B 1
dir /a-d /s /b "%~1" > nul 2>&1
if errorlevel 1 exit /B 0
exit /B 1
:== ERROR_USAGE
echo.
echo. %0
echo. usage:
echo.
echo. Call with a path name.
echo. If it is a directory, and it is empty, the ERRORLEVEL will be left 0
echo. If it is not empty, the ERRORLEVEL will be 1
echo. If it is a file, ERRORLEVEL will be 1
echo. If it does not exist, ERRORLEVEL will be 1
pause
exit /B
@echo off
echo.
echo. This will DELETE DIRECTORIES on your hard drive that are empty
echo.
echo. Call this batch file either
echo. - from the directory you want to clean recursively
echo. - with the directory name specified as the first parameter
echo.
echo. Directories to be deleted will be added to list in a temporary file:
echo. %TMP%\toremovedir.txt
echo.
echo. You will be prompted before the list is processed for delete.
echo.
pause
: clear list
echo.> %TMP%\toremovedir.txt
: search for files to add to list
echo.
if (%1)==() (
set ROOTDIR=.
) else (
set ROOTDIR="%~f1"
)
for /r %ROOTDIR% %%X in (.) do call :GOSUB_CHECKDIR "%%~fX"
echo.
echo. The following directories were found empty and will be deleted
echo. ----------------------------------------------------------------------
type %TMP%\toremovedir.txt
echo. ----------------------------------------------------------------------
echo.
echo. Ready to launch the text file for review / editing
echo. Any changes to the file will be used after you confirm.
echo.
pause
%TMP%\toremovedir.txt
:CONFIRM
echo.
echo. Are you certain you wish to remove the directories?
echo. Close this shell, window, or press CTRL-C to quit.
set RESPONSE=preset-as-no
set /p RESPONSE=Enter 'yes' to confirm delete:
if (%RESPONSE%)==(no) goto CLEANUP
if NOT (%RESPONSE%)==(yes) goto CONFIRM
: delete directories in list
echo.
: sort list first to have sub directories deleted first. avoids errors.
sort /r %TMP%\toremovedir.txt /o %TMP%\toremovedir.txt
for /F "delims=" %%X in (%TMP%\toremovedir.txt) do call :GOSUB_DELETEDIR %%X
:CLEANUP
echo. For your reference, you may wish to copy the file just used:
echo. %TMP%\toremovedir.txt
echo.
echo done.
pause
exit /b
:GOSUB_CHECKDIR
echo checking: %1
call isemptydirectory.bat %1
if ERRORLEVEL 1 exit /b
echo will delete: %~f1
echo %1 >> %TMP%\toremovedir.txt
exit /b
:GOSUB_DELETEDIR
echo DELETING %1
rmdir /s /q %1
exit /b