Пример #1
0
VDStringW VDGetLocalAppDataPath() {
	int csidl = CSIDL_APPDATA;

	HMODULE hmodShell32 = VDLoadSystemLibraryW32("shell32");

	if (hmodShell32) {
		typedef HRESULT (CALLBACK *tpDllGetVersion)(DLLVERSIONINFO *);

		DLLVERSIONINFO dvi = {sizeof(DLLVERSIONINFO)};

		tpDllGetVersion pDllGetVersion = (tpDllGetVersion)GetProcAddress(hmodShell32, "DllGetVersion");
		if (pDllGetVersion && NOERROR == pDllGetVersion(&dvi)) {
			if (dvi.dwMajorVersion >= 5)
				csidl = CSIDL_LOCAL_APPDATA;
		}

		FreeLibrary(hmodShell32);
	}

	if (VDIsWindowsNT()) {
		wchar_t pathW[MAX_PATH];

		if (!SHGetSpecialFolderPathW(NULL, pathW, csidl, FALSE))
			return VDGetProgramPath();

		return VDStringW(pathW);
	} else {
		char pathA[MAX_PATH];

		if (!SHGetSpecialFolderPathA(NULL, pathA, csidl, FALSE))
			return VDGetProgramPath();

		return VDTextAToW(pathA);
	}
}
Пример #2
0
void JobAddConfiguration(const DubOptions *opt, const wchar_t *szFileInput, const wchar_t *pszInputDriver, const wchar_t *szFileOutput, bool fCompatibility, List2<InputFilenameNode> *pListAppended, long lSpillThreshold, long lSpillFrameThreshold, bool bIncludeEditList) {
	JobScriptOutput output;

	JobAddConfigurationInputs(output, szFileInput, pszInputDriver, pListAppended);
	JobCreateScript(output, opt, bIncludeEditList);
	JobAddReloadMarker(output);

	// Add actual run option
	if (lSpillThreshold)
		output.addf("VirtualDub.SaveSegmentedAVI(\"%s\", %d, %d);", strCify(VDTextWToU8(VDStringW(szFileOutput)).c_str()), lSpillThreshold, lSpillFrameThreshold);
	else
		output.addf("VirtualDub.Save%sAVI(\"%s\");", fCompatibility ? "Compatible" : "", strCify(VDTextWToU8(VDStringW(szFileOutput)).c_str()));

	JobAddClose(output);

	///////////////////

	vdautoptr<VDJob> vdj(new VDJob);
	vdj->SetInputFile(szFileInput);
	vdj->SetOutputFile(szFileOutput);

	const JobScriptOutput::Script& script = output.getscript();
	vdj->SetScript(script.data(), script.size(), true);
	g_VDJobQueue.Add(vdj.release(), false);
}
Пример #3
0
void JobAddConfigurationImages(const DubOptions *opt, const wchar_t *szFileInput, const wchar_t *pszInputDriver, const wchar_t *szFilePrefix, const wchar_t *szFileSuffix, int minDigits, int imageFormat, int quality, List2<InputFilenameNode> *pListAppended) {
	JobScriptOutput output;

	JobAddConfigurationInputs(output, szFileInput, pszInputDriver, pListAppended);
	JobCreateScript(output, opt);
	JobAddReloadMarker(output);

	// Add actual run option
	VDStringA s(strCify(VDTextWToU8(VDStringW(szFilePrefix)).c_str()));

	output.addf("VirtualDub.SaveImageSequence(\"%s\", \"%s\", %d, %d, %d);", s.c_str(), strCify(VDTextWToU8(VDStringW(szFileSuffix)).c_str()), minDigits, imageFormat, quality);

	JobAddClose(output);

	///////////////////

	vdautoptr<VDJob> vdj(new VDJob);
	vdj->SetInputFile(szFileInput);
	VDStringW outputFile;
	outputFile.sprintf(L"%ls*%ls", szFilePrefix, szFileSuffix);
	vdj->SetOutputFile(outputFile.c_str());

	const JobScriptOutput::Script& script = output.getscript();
	vdj->SetScript(script.data(), script.size(), true);
	g_VDJobQueue.Add(vdj.release(), false);
}
Пример #4
0
void VDRegistryProviderMemory::Value::SetBinary(const void *p, size_t len) {
	if (mType != kTypeBinary) {
		mString.swap(VDStringW());
		mType = kTypeBinary;
	}

	mRawData.assign((char *)p, (char *)p + len);
}
Пример #5
0
VDStringW VDGetSystemPathNT() {
    wchar_t path[MAX_PATH];

    if (!GetSystemDirectoryW(path, MAX_PATH))
        throw MyWin32Error("Cannot locate system directory: %%s", GetLastError());

    return VDStringW(path);
}
Пример #6
0
void VDRegistryProviderMemory::Value::SetInt(sint32 v) {
	if (mType != kTypeInt) {
		mString.swap(VDStringW());
		mRawData.swap(vdfastvector<char>());
		mType = kTypeInt;
	}

	mInt = v;
}
Пример #7
0
VDStringW VDFileGetRelativePath(const wchar_t *basePath, const wchar_t *pathToConvert, bool allowAscent) {
    VDParsedPath base(basePath);
    VDParsedPath path(pathToConvert);

    // Fail if either path is relative.
    if (base.IsRelative() || path.IsRelative())
        return VDStringW();

    // Fail if the roots don't match.
    if (vdwcsicmp(base.GetRoot(), path.GetRoot()))
        return VDStringW();

    // Figure out how many components are in common.
    size_t n1 = base.GetComponentCount();
    size_t n2 = path.GetComponentCount();
    size_t nc = 0;

    while(nc < n1 && nc < n2 && !vdwcsicmp(base.GetComponent(nc), path.GetComponent(nc)))
        ++nc;

    // Check how many extra components are in the base; these need to become .. identifiers.
    VDParsedPath relPath;

    if (n1 > nc) {
        if (!allowAscent)
            return VDStringW();

        while(n1 > nc) {
            relPath.AddComponent(L"..");
            --n1;
        }
    }

    // Append extra components from path.
    while(nc < n2) {
        relPath.AddComponent(path.GetComponent(nc++));
    }

    // Copy stream.
    relPath.SetStream(path.GetStream());

    return relPath.ToString();
}
Пример #8
0
VDStringW VDGetFullPath(const wchar_t *partialPath) {
    static tpGetFullPathNameW spGetFullPathNameW = (tpGetFullPathNameW)GetProcAddress(GetModuleHandle("kernel32.dll"), "GetFullPathNameW");

    union {
        char		a[MAX_PATH];
        wchar_t		w[MAX_PATH];
    } tmpBuf;

    if (spGetFullPathNameW && !(GetVersion() & 0x80000000)) {
        LPWSTR p;

        tmpBuf.w[0] = 0;
        DWORD count = spGetFullPathNameW(partialPath, MAX_PATH, tmpBuf.w, &p);

        if (count < MAX_PATH)
            return VDStringW(tmpBuf.w);

        VDStringW tmp(count);

        DWORD newCount = spGetFullPathNameW(partialPath, count, (wchar_t *)tmp.data(), &p);
        if (newCount < count)
            return tmp;

        return VDStringW(partialPath);
    } else {
        LPSTR p;
        VDStringA pathA(VDTextWToA(partialPath));

        tmpBuf.a[0] = 0;
        DWORD count = GetFullPathNameA(pathA.c_str(), MAX_PATH, tmpBuf.a, &p);

        if (count < MAX_PATH)
            return VDStringW(VDTextAToW(tmpBuf.a));

        VDStringA tmpA(count);

        DWORD newCount = GetFullPathNameA(pathA.c_str(), count, (char *)tmpA.data(), &p);
        if (newCount < count)
            return VDTextAToW(tmpA);

        return VDStringW(partialPath);
    }
}
Пример #9
0
void VDShowHelp(HWND hwnd, const wchar_t *filename) {
	try {
		VDStringW helpFile(VDGetHelpPath());

		if (!VDDoesPathExist(helpFile.c_str()))
			throw MyError("Cannot find help file: %ls", helpFile.c_str());

		// If we're on Windows NT, check for the ADS and/or network drive.
		if (VDIsWindowsNT()) {
			VDStringW helpFileADS(helpFile);
			helpFileADS += L":Zone.Identifier";
			if (VDDoesPathExist(helpFileADS.c_str())) {
				int rv = MessageBox(hwnd, "VirtualDub has detected that its help file, VirtualDub.chm, has an Internet Explorer download location marker on it. This may prevent the help file from being displayed properly, resulting in \"Action canceled\" errors being displayed. Would you like to remove it?", "VirtualDub warning", MB_YESNO|MB_ICONEXCLAMATION);

				if (rv == IDYES)
					DeleteFileW(helpFileADS.c_str());
			}
		}

		if (filename) {
			helpFile.append(L"::/");
			helpFile.append(filename);
		}

		VDStringW helpCommand(VDStringW(L"\"hh.exe\" \"") + helpFile + L'"');

		PROCESS_INFORMATION pi;
		BOOL retval;

		// CreateProcess will actually modify the string that it gets, soo....
		if (VDIsWindowsNT()) {
			STARTUPINFOW si = {sizeof(STARTUPINFOW)};
			std::vector<wchar_t> tempbufW(helpCommand.size() + 1, 0);
			helpCommand.copy(&tempbufW[0], tempbufW.size());
			retval = CreateProcessW(NULL, &tempbufW[0], NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE, NULL, NULL, &si, &pi);
		} else {
			STARTUPINFOA si = {sizeof(STARTUPINFOA)};
			VDStringA strA(VDTextWToA(helpCommand));
			std::vector<char> tempbufA(strA.size() + 1, 0);
			strA.copy(&tempbufA[0], tempbufA.size());
			retval = CreateProcessA(NULL, &tempbufA[0], NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE, NULL, NULL, &si, &pi);
		}

		if (retval) {
			CloseHandle(pi.hThread);
			CloseHandle(pi.hProcess);
		} else
			throw MyWin32Error("Cannot launch HTML Help: %%s", GetLastError());
	} catch(const MyError& e) {
		e.post(hwnd, g_szError);
	}
}
Пример #10
0
void JobAddConfigurationInputs(JobScriptOutput& output, const wchar_t *szFileInput, const wchar_t *pszInputDriver, List2<InputFilenameNode> *pListAppended) {
	do {
		const VDStringA filename(strCify(VDTextWToU8(VDStringW(szFileInput)).c_str()));

		if (g_pInputOpts) {
			int req = g_pInputOpts->write(NULL, 0);

			vdfastvector<char> srcbuf(req);

			int srcsize = g_pInputOpts->write(srcbuf.data(), req);

			if (srcsize) {
				vdfastvector<char> encbuf((srcsize + 2) / 3 * 4 + 1);
				membase64(encbuf.data(), srcbuf.data(), srcsize);

				const VDStringA filename(strCify(VDTextWToU8(VDStringW(szFileInput)).c_str()));

				output.addf("VirtualDub.Open(\"%s\",\"%s\",0,\"%s\");", filename.c_str(), pszInputDriver?strCify(VDTextWToU8(VDStringW(pszInputDriver)).c_str()):"", encbuf.data());
				break;
			}
		}

		output.addf("VirtualDub.Open(\"%s\",\"%s\",0);", filename.c_str(), pszInputDriver?strCify(VDTextWToU8(VDStringW(pszInputDriver)).c_str()):"");

	} while(false);

	if (pListAppended) {
		InputFilenameNode *ifn = pListAppended->AtHead(), *ifn_next;

		if (ifn = ifn->NextFromHead())
			while(ifn_next = ifn->NextFromHead()) {
				output.addf("VirtualDub.Append(\"%s\");", strCify(VDTextWToU8(VDStringW(ifn->name)).c_str()));
				ifn = ifn_next;
			}
	}
}
Пример #11
0
VDStringW VDMakePath(const wchar_t *base, const wchar_t *file) {
    if (!*base)
        return VDStringW(file);

    VDStringW result(base);

    const wchar_t c = result[result.size() - 1];

    if (c != L'/' && c != L'\\' && c != L':')
        result += L'\\';

    result.append(file);

    return result;
}
Пример #12
0
VDStringW VDFileGetRootPath(const wchar_t *path) {
    static tpGetVolumePathNameW spGetVolumePathNameW = (tpGetVolumePathNameW)GetProcAddress(GetModuleHandle("kernel32.dll"), "GetVolumePathNameW");
    static tpGetFullPathNameW spGetFullPathNameW = (tpGetFullPathNameW)GetProcAddress(GetModuleHandle("kernel32.dll"), "GetFullPathNameW");

    VDStringW fullPath(VDGetFullPath(path));

    // Windows 2000/XP path
    if (spGetVolumePathNameW) {
        vdblock<wchar_t> buf(std::max<size_t>(fullPath.size() + 1, MAX_PATH));

        if (spGetVolumePathNameW(path, buf.data(), buf.size()))
            return VDStringW(buf.data());
    }

    // Windows 95/98/ME/NT4 path
    const wchar_t *s = fullPath.c_str();
    VDStringW root(s, VDFileSplitRoot(s) - s);
    VDFileFixDirPath(root);
    return root;
}
Пример #13
0
VDStringW VDGetWindowTextW32(HWND hwnd) {
	union {
		wchar_t w[256];
		char a[512];
	} buf;

	if (VDIsWindowsNT()) {
		int len = GetWindowTextLengthW(hwnd);

		if (len > 255) {
			vdblock<wchar_t> tmp(len + 1);
			len = GetWindowTextW(hwnd, tmp.data(), tmp.size());

			VDStringW text(tmp.data(), len);
			return text;
		} else if (len > 0) {
			len = GetWindowTextW(hwnd, buf.w, 256);

			VDStringW text(buf.w, len);
			return text;
		}
	} else {
		int len = GetWindowTextLengthA(hwnd);

		if (len > 511) {
			vdblock<char> tmp(len + 1);
			len = GetWindowTextA(hwnd, tmp.data(), tmp.size());

			VDStringW text(VDTextAToW(tmp.data(), len));
			return text;
		} else if (len > 0) {
			len = GetWindowTextA(hwnd, buf.a, 512);

			VDStringW text(VDTextAToW(buf.a, len));
			return text;
		}
	}

	return VDStringW();
}
Пример #14
0
void VDRemoveDirectory(const wchar_t *path) {
    VDStringW::size_type l(wcslen(path));

    if (l) {
        const wchar_t c = path[l-1];

        if (c == L'/' || c == L'\\') {
            VDCreateDirectory(VDStringW(path, l-1).c_str());
            return;
        }
    }

    BOOL succeeded;

    if (!(GetVersion() & 0x80000000)) {
        succeeded = RemoveDirectoryW(path);
    } else {
        succeeded = RemoveDirectoryA(VDTextWToA(path).c_str());
    }

    if (!succeeded)
        throw MyWin32Error("Cannot remove directory: %%s", GetLastError());
}
Пример #15
0
void JobAddConfigurationSaveAudio(const DubOptions *opt, const wchar_t *srcFile, const wchar_t *srcInputDriver, List2<InputFilenameNode> *pListAppended, const wchar_t *dstFile, bool raw, bool includeEditList) {
	JobScriptOutput output;

	JobAddConfigurationInputs(output, srcFile, srcInputDriver, pListAppended);
	JobCreateScript(output, opt, includeEditList);
	JobAddReloadMarker(output);

	// Add actual run option
	output.addf("VirtualDub.Save%s(\"%s\");", raw ? "RawAudio" : "WAV", strCify(VDTextWToU8(VDStringW(dstFile)).c_str()));

	JobAddClose(output);

	///////////////////

	vdautoptr<VDJob> vdj(new VDJob);
	vdj->SetInputFile(srcFile);
	vdj->SetOutputFile(dstFile);

	const JobScriptOutput::Script& script = output.getscript();
	vdj->SetScript(script.data(), script.size(), true);
	g_VDJobQueue.Add(vdj.release(), false);
}
Пример #16
0
VDStringW VDFileResolvePath(const wchar_t *basePath, const wchar_t *pathToResolve) {
    if (VDFileIsRelativePath(pathToResolve))
        return VDFileGetCanonicalPath(VDMakePath(basePath, pathToResolve).c_str());

    return VDStringW(pathToResolve);
}
Пример #17
0
void JobCreateScript(JobScriptOutput& output, const DubOptions *opt, bool bIncludeEditList = true, bool bIncludeTextInfo = true) {
	char *mem= NULL;
	char buf[4096];
	long l;

	int audioSourceMode = g_project->GetAudioSourceMode();

	switch(audioSourceMode) {

	case kVDAudioSourceMode_External:
		{
			const VDStringA& encodedFileName = VDEncodeScriptString(VDStringW(g_szInputWAVFile));
			const VDStringA& encodedDriverName = VDEncodeScriptString(VDTextWToU8(g_project->GetAudioSourceDriverName(), -1));

			// check if we have options to write out
			const InputFileOptions *opts = g_project->GetAudioSourceOptions();
			if (opts) {
				int l;
				char buf[256];

				l = opts->write(buf, (sizeof buf)/7*3);

				if (l) {
					membase64(buf+l, (char *)buf, l);

					output.addf("VirtualDub.audio.SetSource(\"%s\", \"%s\", \"%s\");", encodedFileName.c_str(), encodedDriverName.c_str(), buf+l);
					break;
				}
			}

			// no options
			output.addf("VirtualDub.audio.SetSource(\"%s\", \"%s\");", encodedFileName.c_str(), encodedDriverName.c_str());
		}
		break;

	default:
		if (audioSourceMode >= kVDAudioSourceMode_Source) {
			int index = audioSourceMode - kVDAudioSourceMode_Source;

			if (!index)
				output.addf("VirtualDub.audio.SetSource(1);");
			else
				output.addf("VirtualDub.audio.SetSource(1,%d);", index);
			break;
		}
		// fall through
	case kVDAudioSourceMode_None:
		output.addf("VirtualDub.audio.SetSource(0);");
		break;
	
	}

	output.addf("VirtualDub.audio.SetMode(%d);", opt->audio.mode);

	output.addf("VirtualDub.audio.SetInterleave(%d,%d,%d,%d,%d);",
			opt->audio.enabled,
			opt->audio.preload,
			opt->audio.interval,
			opt->audio.is_ms,
			opt->audio.offset);

	output.addf("VirtualDub.audio.SetClipMode(%d,%d);",
			opt->audio.fStartAudio,
			opt->audio.fEndAudio);

	output.addf("VirtualDub.audio.SetConversion(%d,%d,%d,0,%d);",
			opt->audio.new_rate,
			opt->audio.newPrecision,
			opt->audio.newChannels,
			opt->audio.fHighQuality);

	if (opt->audio.mVolume >= 0.0f)
		output.addf("VirtualDub.audio.SetVolume(%d);", VDRoundToInt(256.0f * opt->audio.mVolume));
	else
		output.addf("VirtualDub.audio.SetVolume();");

	if (g_ACompressionFormat) {
		if (g_ACompressionFormat->mExtraSize) {
			mem = (char *)allocmem(((g_ACompressionFormat->mExtraSize+2)/3)*4 + 1);
			if (!mem) throw MyMemoryError();

			membase64(mem, (char *)(g_ACompressionFormat+1), g_ACompressionFormat->mExtraSize);
			output.addf("VirtualDub.audio.SetCompressionWithHint(%d,%d,%d,%d,%d,%d,%d,\"%s\",\"%s\");"
						,g_ACompressionFormat->mTag
						,g_ACompressionFormat->mSamplingRate
						,g_ACompressionFormat->mChannels
						,g_ACompressionFormat->mSampleBits
						,g_ACompressionFormat->mDataRate
						,g_ACompressionFormat->mBlockSize
						,g_ACompressionFormat->mExtraSize
						,mem
						,VDEncodeScriptString(g_ACompressionFormatHint).c_str()
						);

			freemem(mem);
		} else
			output.addf("VirtualDub.audio.SetCompressionWithHint(%d,%d,%d,%d,%d,%d,\"%s\");"
						,g_ACompressionFormat->mTag
						,g_ACompressionFormat->mSamplingRate
						,g_ACompressionFormat->mChannels
						,g_ACompressionFormat->mSampleBits
						,g_ACompressionFormat->mDataRate
						,g_ACompressionFormat->mBlockSize
						,VDEncodeScriptString(g_ACompressionFormatHint).c_str()
						);
	} else
		output.addf("VirtualDub.audio.SetCompression();");

	output.addf("VirtualDub.audio.EnableFilterGraph(%d);", opt->audio.bUseAudioFilterGraph);

	output.addf("VirtualDub.video.SetInputFormat(%d);", opt->video.mInputFormat);
	output.addf("VirtualDub.video.SetOutputFormat(%d);", opt->video.mOutputFormat);

	output.addf("VirtualDub.video.SetMode(%d);", opt->video.mode);
	output.addf("VirtualDub.video.SetSmartRendering(%d);", opt->video.mbUseSmartRendering);
	output.addf("VirtualDub.video.SetPreserveEmptyFrames(%d);", opt->video.mbPreserveEmptyFrames);

	output.addf("VirtualDub.video.SetFrameRate2(%u,%u,%d);",
			opt->video.mFrameRateAdjustHi,
			opt->video.mFrameRateAdjustLo,
			opt->video.frameRateDecimation);

	if (opt->video.frameRateTargetLo) {
		output.addf("VirtualDub.video.SetTargetFrameRate(%u,%u);",
				opt->video.frameRateTargetHi,
				opt->video.frameRateTargetLo);
	}

	output.addf("VirtualDub.video.SetIVTC(0, 0, 0, 0);");

	if ((g_Vcompression.dwFlags & ICMF_COMPVARS_VALID) && g_Vcompression.fccHandler) {
		output.addf("VirtualDub.video.SetCompression(0x%08lx,%d,%d,%d);",
				g_Vcompression.fccHandler,
				g_Vcompression.lKey,
				g_Vcompression.lQ,
				g_Vcompression.lDataRate);

		l = ICGetStateSize(g_Vcompression.hic);

		if (l>0) {
			mem = (char *)allocmem(l + ((l+2)/3)*4 + 1);
			if (!mem) throw MyMemoryError();

			if (ICGetState(g_Vcompression.hic, mem, l)<0) {
				freemem(mem);
//				throw MyError("Bad state data returned from compressor");

				// Fine then, be that way.  Stupid Pinnacle DV200 driver.
				mem = NULL;
			}

			if (mem) {
				membase64(mem+l, mem, l);
				// urk... Windows Media 9 VCM uses a very large configuration struct (~7K pre-BASE64).
				sprintf(buf, "VirtualDub.video.SetCompData(%d,\"", l);

				VDStringA line(buf);
				line += (mem+l);
				line += "\");";
				output.adds(line.c_str());
				freemem(mem);
			}
		}

	} else
		output.addf("VirtualDub.video.SetCompression();");

	output.addf("VirtualDub.video.filters.Clear();");

	// Add video filters

	FilterInstance *fa = (FilterInstance *)g_listFA.tail.next, *fa_next;
	int iFilter = 0;

	while(fa_next = (FilterInstance *)fa->next) {
		output.addf("VirtualDub.video.filters.Add(\"%s\");", strCify(fa->GetName()));

		if (fa->IsCroppingEnabled()) {
			const vdrect32& cropInsets = fa->GetCropInsets();

			output.addf("VirtualDub.video.filters.instance[%d].SetClipping(%d,%d,%d,%d%s);"
						, iFilter
						, cropInsets.left
						, cropInsets.top
						, cropInsets.right
						, cropInsets.bottom
						, fa->IsPreciseCroppingEnabled() ? "" : ",0"
						);
		}

		VDStringA scriptStr;
		if (fa->GetScriptString(scriptStr))
			output.addf("VirtualDub.video.filters.instance[%d].%s;", iFilter, scriptStr.c_str());

		if (!fa->IsEnabled())
			output.addf("VirtualDub.video.filters.instance[%d].SetEnabled(false);", iFilter);

		VDParameterCurve *pc = fa->GetAlphaParameterCurve();
		if (pc) {
			output.addf("declare curve = VirtualDub.video.filters.instance[%d].AddOpacityCurve();", iFilter);

			const VDParameterCurve::PointList& pts = pc->Points();
			for(VDParameterCurve::PointList::const_iterator it(pts.begin()), itEnd(pts.end()); it!=itEnd; ++it) {
				const VDParameterCurvePoint& pt = *it;

				output.addf("curve.AddPoint(%g, %g, %d);", pt.mX, pt.mY, pt.mbLinear);
			}
		}

		++iFilter;
		fa = fa_next;
	}

	// Add audio filters

	{
		VDAudioFilterGraph::FilterList::const_iterator it(g_audioFilterGraph.mFilters.begin()), itEnd(g_audioFilterGraph.mFilters.end());
		int connidx = 0;
		int srcfilt = 0;

		output.addf("VirtualDub.audio.filters.Clear();");

		for(; it!=itEnd; ++it, ++srcfilt) {
			const VDAudioFilterGraph::FilterEntry& fe = *it;

			output.addf("VirtualDub.audio.filters.Add(\"%s\");", strCify(VDTextWToU8(fe.mFilterName).c_str()));

			for(unsigned i=0; i<fe.mInputPins; ++i) {
				const VDAudioFilterGraph::FilterConnection& conn = g_audioFilterGraph.mConnections[connidx++];
				output.addf("VirtualDub.audio.filters.Connect(%d, %d, %d, %d);", conn.filt, conn.pin, srcfilt, i);
			}

			VDPluginConfig::const_iterator itc(fe.mConfig.begin()), itcEnd(fe.mConfig.end());

			for(; itc!=itcEnd; ++itc) {
				const unsigned idx = (*itc).first;
				const VDPluginConfigVariant& var = (*itc).second;

				switch(var.GetType()) {
				case VDPluginConfigVariant::kTypeU32:
					output.addf("VirtualDub.audio.filters.instance[%d].SetInt(%d, %d);", srcfilt, idx, var.GetU32());
					break;
				case VDPluginConfigVariant::kTypeS32:
					output.addf("VirtualDub.audio.filters.instance[%d].SetInt(%d, %d);", srcfilt, idx, var.GetS32());
					break;
				case VDPluginConfigVariant::kTypeU64:
					output.addf("VirtualDub.audio.filters.instance[%d].SetLong(%d, %I64d);", srcfilt, idx, var.GetU64());
					break;
				case VDPluginConfigVariant::kTypeS64:
					output.addf("VirtualDub.audio.filters.instance[%d].SetLong(%d, %I64d);", srcfilt, idx, var.GetS64());
					break;
				case VDPluginConfigVariant::kTypeDouble:
					output.addf("VirtualDub.audio.filters.instance[%d].SetDouble(%d, %g);", srcfilt, idx, var.GetDouble());
					break;
				case VDPluginConfigVariant::kTypeAStr:
					output.addf("VirtualDub.audio.filters.instance[%d].SetString(%d, \"%s\");", srcfilt, idx, strCify(VDTextWToU8(VDTextAToW(var.GetAStr())).c_str()));
					break;
				case VDPluginConfigVariant::kTypeWStr:
					output.addf("VirtualDub.audio.filters.instance[%d].SetString(%d, \"%s\");", srcfilt, idx, strCify(VDTextWToU8(var.GetWStr(), -1).c_str()));
					break;
				case VDPluginConfigVariant::kTypeBlock:
					output.addf("VirtualDub.audio.filters.instance[%d].SetBlock(%d, %d, \"%s\");", srcfilt, idx, var.GetBlockLen(), VDEncodeBase64A(var.GetBlockPtr(), var.GetBlockLen()).c_str());
					break;
				}
			}
		}
	}

	// Add subset information

	if (bIncludeEditList) {
		const FrameSubset& fs = g_project->GetTimeline().GetSubset();

		output.addf("VirtualDub.subset.Clear();");

		for(FrameSubset::const_iterator it(fs.begin()), itEnd(fs.end()); it!=itEnd; ++it)
			output.addf("VirtualDub.subset.Add%sRange(%I64d,%I64d);", it->bMask ? "Masked" : "", it->start, it->len);

		// Note that this must be AFTER the subset (we used to place it before, which was a bug).
		if (g_project->IsSelectionPresent()) {
			output.addf("VirtualDub.video.SetRangeFrames(%I64d,%I64d);",
				g_project->GetSelectionStartFrame(),
				g_project->GetSelectionEndFrame());
		} else {
			output.addf("VirtualDub.video.SetRange();");
		}
	}

	// Add text information
	if (bIncludeTextInfo) {
		typedef std::list<std::pair<uint32, VDStringA> > tTextInfo;
		const tTextInfo& textInfo = g_project->GetTextInfo();

		output.addf("VirtualDub.project.ClearTextInfo();");
		for(tTextInfo::const_iterator it(textInfo.begin()), itEnd(textInfo.end()); it!=itEnd; ++it) {
			char buf[5]={0};
			
			memcpy(buf, &(*it).first, 4);

			output.addf("VirtualDub.project.AddTextInfo(\"%s\", \"%s\");", buf, VDEncodeScriptString((*it).second).c_str());
		}
	}
}