#include "texception.h"
#include "tpropertytype.h"
//#include "timageinfo.h"
//#include "tlevel_io.h"
#include "tproperty.h"
#include "tiio.h"
#if !(defined(x64) || defined(__LP64__) || defined(LINUX))
//*******************************************************************************
// 32-bit version
//*******************************************************************************
#ifdef _WIN32
#ifdef _WIN32
#pragma warning(disable : 4996)
#endif
#define list List
#define map Map
#define iterator Iterator
#define float_t Float_t
#define int_fast8_t QT_int_fast8_t
#define int_fast16_t QT_int_fast16_t
#define uint_fast16_t QT_uint_fast16_t
#include "QTML.h"
#include "Movies.h"
#include "Script.h"
#include "FixMath.h"
#include "Sound.h"
#include "QuickTimeComponents.h"
#include "tquicktime.h"
#undef list
#undef map
#undef iterator
#undef float_t
#undef QT_int_fast8_t
#undef QT_int_fast16_t
#undef QT_uint_fast16_t
#else
#define list List
#define map Map
#define iterator Iterator
#define float_t Float_t
#include <Carbon/Carbon.h>
#include <QuickTime/Movies.h>
#include <QuickTime/ImageCompression.h>
#include <QuickTime/QuickTimeComponents.h>
#undef list
#undef map
#undef iterator
#undef float_t
#endif
/*
questo file gestisce il salvataggio in un .tnz e il caricamento dei setting dei mov.
viene usato il popup fornito da quicktime, con tutti i suoi setting e i sotto settings.
i setting sono memorizzati da quicktime in un componentInstance. Da qui, possono essere convertiti in un atomContainer,
che e' una struttura simile alla nostra propertyGroup, ma con gli atomi strutturati ad albero.
sono state scritte due funzioni di conversione da atomContainer a propertygroup e viceversa
ogni atom ha un type, id, e numero figli. se numero figli=0 allora l'atomo e'una foglia,
e quindi ha un buffer di dati di valori char.
ogni atomo viene trasformato in una stringProperty. il nome della stringProperty e'
"type id numeroFigli"
se numerofigli>0, allora la stringProperty ha un valore nullo, e le prossime
numerofigli property contengono i figli;
se numerofigli==0, allora il valore della property contiene il buffer di dati,
convertito in stringa.
ecco coem viene convertito il buffer in stringa:
se ad esempio il buffer e' composto di 3 bytes, buf[0] = 13 buf[1]=0 buf[2]=231 allora la strnga valore sara' "13 0 231"
se ci sono piu 0 consecutivi, vengono memorizzati per salvare spazio come "z count" in cui count e' il numero di 0.
esempio: buf[0] = 13 buf[1]=0 buf[2]=0 buf[3]=0 buf[4]=0 buf5]=231
allora str = "13 z 4 231"
*/
#include "movsettings.h"
//------------------------------------------------
void visitAtoms(const QTAtomContainer &atoms, const QTAtom &parent, TPropertyGroup &pg)
{
QTAtom curr = 0;
do {
if (QTNextChildAnyType(atoms, parent, curr, &curr) != noErr)
assert(false);
if (curr == 0)
break;
QTAtomType atomType;
QTAtomID id;
QTGetAtomTypeAndID(atoms, curr, &atomType, &id);
int sonCount = QTCountChildrenOfType(atoms, curr, 0);
char buffer[1024];
sprintf(buffer, "%d %d %d", (int)atomType, (int)id, sonCount);
string str(buffer);
if (sonCount > 0) {
pg.add(new TStringProperty(str, TString()));
visitAtoms(atoms, curr, pg);
}
else {
long size;
UCHAR *atomData;
if (QTGetAtomDataPtr(atoms, curr, &size, (char **)&atomData) != noErr)
assert(false);
string strapp;
for (int i = 0; i < size; i++) {
string num;
if (atomData[i] == 0) {
int count = 1;
while ((i + 1) < size && atomData[i + 1] == 0)
i++, count++;
if (count > 1) {
num = std::to_string(count);
strapp = strapp + "z " + num + " ";
continue;
}
}
num = std::to_string(atomData[i]);
strapp = strapp + string(num) + " ";
}
//unsigned short*buffer = new unsigned short[size];
//buffer[size]=0;
//for (i=0; i<size; i++)
// buffer[i] = atomData[i]+1;
wstring data = ::to_wstring(strapp);
pg.add(new TStringProperty(str, data));
}
} while (curr != 0);
}
//------------------------------------------------
namespace
{
void compareAtoms(const QTAtomContainer &atoms1, QTAtom parent1, const QTAtomContainer &atoms2, QTAtom parent2)
{
QTAtom curr1 = 0, curr2 = 0;
assert(QTCountChildrenOfType(atoms1, parent1, 0) == QTCountChildrenOfType(atoms2, parent2, 0));
do {
if (QTNextChildAnyType(atoms1, parent1, curr1, &curr1) != noErr)
assert(false);
if (QTNextChildAnyType(atoms2, parent2, curr2, &curr2) != noErr)
assert(false);
assert((curr1 != 0 && curr2 != 0) || (curr1 == 0 && curr2 == 0));
if (curr1 == 0 || curr2 == 0)
break;
QTAtomType atomType1, atomType2;
QTAtomID id1, id2;
QTGetAtomTypeAndID(atoms1, curr1, &atomType1, &id1);
QTGetAtomTypeAndID(atoms2, curr2, &atomType2, &id2);
assert(atomType1 == atomType2);
int sonCount1 = QTCountChildrenOfType(atoms1, curr1, 0);
int sonCount2 = QTCountChildrenOfType(atoms2, curr2, 0);
assert(sonCount1 == sonCount2);
if (sonCount1 > 0)
compareAtoms(atoms1, curr1, atoms2, curr2);
else {
long size1;
UCHAR *atomData1;
long size2;
UCHAR *atomData2;
if (QTGetAtomDataPtr(atoms1, curr1, &size1, (char **)&atomData1) != noErr)
assert(false);
if (QTGetAtomDataPtr(atoms2, curr2, &size2, (char **)&atomData2) != noErr)
assert(false);
assert(size1 == size2);
for (int i = 0; i < size1; i++)
assert(atomData1[i] == atomData2[i]);
}
} while (curr1 != 0 && curr2 != 0);
}
}
//------------------------------------------------
void fromAtomsToProperties(const QTAtomContainer &atoms, TPropertyGroup &pg)
{
pg.clear();
visitAtoms(atoms, kParentAtomIsContainer, pg);
}
//------------------------------------------------
void visitprops(TPropertyGroup &pg, int &index, QTAtomContainer &atoms, QTAtom parent)
{
int count = pg.getPropertyCount();
while (index < count) {
TStringProperty *p = (TStringProperty *)pg.getProperty(index++);
string str0 = p->getName();
const char *buf = str0.c_str();
int atomType, id, sonCount;
sscanf(buf, "%d %d %d", &atomType, &id, &sonCount);
QTAtom newAtom;
if (sonCount == 0) {
wstring appow = p->getValue();
string appo = ::to_string(appow);
const char *str = appo.c_str();
vector<UCHAR> buf;
while (strlen(str) > 0) {
if (str[0] == 'z') {
int count = atoi(str + 1);
str += (count < 10) ? 4 : ((count < 100) ? 5 : 6);
while (count--)
buf.push_back(0);
} else {
int val = atoi(str);
assert(val >= 0 && val < 256);
str += (val < 10) ? 2 : ((val < 100) ? 3 : 4);
buf.push_back(val);
}
}
//const unsigned short*bufs = str1.c_str();
//UCHAR *bufc = new UCHAR[size];
//for (int i=0; i<size; i++)
// {
// assert(bufs[i]<257);
// bufc[i] = (UCHAR)(bufs[i]-1);
// }
void *ptr = 0;
if (buf.size() != 0) {
ptr = &(buf[0]);
}
QTInsertChild(atoms, parent, (QTAtomType)atomType, (QTAtomID)id, 0,
buf.size(), (void *)ptr, 0);
} else {
QTInsertChild(atoms, parent, (QTAtomType)atomType, (QTAtomID)id,
0, 0, 0, &newAtom);
visitprops(pg, index, atoms, newAtom);
}
}
}
//------------------------------------------------
void fromPropertiesToAtoms(TPropertyGroup &pg, QTAtomContainer &atoms)
{
int index = 0;
visitprops(pg, index, atoms, kParentAtomIsContainer);
}
//------------------------------------------------
/*
#ifdef MACOSX
SCExtendedProcs gProcStruct, ptr;
static Boolean QTCmpr_FilterProc
(DialogPtr theDialog, EventRecord *theEvent,
short *theItemHit, long theRefCon)
{
#pragma unused(theItemHit, theRefCon)
Boolean myEventHandled = false;
WindowRef myEventWindow = NULL;
WindowRef myDialogWindow = NULL;
myDialogWindow = GetDialogWindow(theDialog);
switch (theEvent->what) {
case updateEvt:
myEventWindow = (WindowRef)theEvent->message;
// Change the window class
HIWindowChangeClass(myEventWindow,kUtilityWindowClass);
// Activate the window scope
SetWindowActivationScope(myEventWindow,kWindowActivationScopeAll);
// Set the brushed metal theme on the window
SetThemeWindowBackground(myEventWindow,kThemeBrushUtilityWindowBackgroundActive,true);
break;
}
return(myEventHandled);
}
#endif
*/
//------------------------------------------------
void openMovSettingsPopup(TPropertyGroup *props, bool macBringToFront)
{
#ifdef _WIN32
if (InitializeQTML(0) != noErr)
return;
#endif
ComponentInstance ci = OpenDefaultComponent(StandardCompressionType, StandardCompressionSubType);
QTAtomContainer atoms;
QTNewAtomContainer(&atoms);
fromPropertiesToAtoms(*props, atoms);
ComponentResult err;
if ((err = SCSetSettingsFromAtomContainer(ci, atoms)) != noErr) {
CloseComponent(ci);
ci = OpenDefaultComponent(StandardCompressionType, StandardCompressionSubType);
assert(false);
}
QTDisposeAtomContainer(atoms);
#ifdef MACOSX
// Install an external procedure to use a callback filter on the request settings dialog
// On MACOSX we need to change the dialog appearance in order to pop-up in front of the
// toonz main window.
/*
gProcStruct.filterProc = NewSCModalFilterUPP(QTCmpr_FilterProc);
// I don't install any hook
gProcStruct.hookProc = NULL;
gProcStruct.customName[0] = 0;
// I don't use refcon
gProcStruct.refcon = 0;
// set the current extended procs
SCSetInfo(ci, scExtendedProcsType, &gProcStruct);
*/
#endif
err = SCRequestSequenceSettings(ci);
//assert(err==noErr);
QTAtomContainer atomsOut;
if (SCGetSettingsAsAtomContainer(ci, &atomsOut) != noErr)
assert(false);
fromAtomsToProperties(atomsOut, *props);
QTDisposeAtomContainer(atomsOut);
CloseComponent(ci);
//int dataSize=0, numChildren = 0, numLevels=0;
//retrieveData(settings, kParentAtomIsContainer, dataSize, numChildren, numLevels);
}
bool Tiio::isQuicktimeInstalled()
{
#ifdef MACOSX
return true;
#else
static int ret = -1;
if (ret == -1)
ret = (InitializeQTML(0) == noErr) ? 1 : 0;
return (ret == 1);
#endif
}
#else //x64
//*******************************************************************************
// 64-bit proxied version
//*******************************************************************************
//Toonz includes
#include "tfilepath.h"
#include "tstream.h"
//tipc includes
#include "tipc.h"
#include "t32bitsrv_wrap.h"
//MAC-Specific includes
#ifdef MACOSX
#include <ApplicationServices/ApplicationServices.h>
#endif
#include "movsettings.h"
//---------------------------------------------------------------------------
//Using 32-bit background server correspondence to achieve the same result
void openMovSettingsPopup(TPropertyGroup *props, bool unused)
{
QLocalSocket socket;
if (!tipc::startSlaveConnection(&socket, t32bitsrv::srvName(), 3000, t32bitsrv::srvCmdline(), "_main"))
return;
//Send the appropriate commands to the server
tipc::Stream stream(&socket);
tipc::Message msg;
//We'll communicate through temporary files.
stream << (msg << QString("$tmpfile_request") << QString("openMovSets"));
QString res(tipc::readMessage(stream, msg));
QString fp;
msg >> fp;
assert(res == "ok" && !fp.isEmpty());
TFilePath tfp(fp.toStdWString());
{
//Save the input props to the temporary file
TOStream os(tfp);
props->saveData(os);
}
//Invoke the settings popup
stream << (msg << tipc::clr << QString("$openMovSettingsPopup") << fp);
res = tipc::readMessageNB(stream, msg, -1, QEventLoop::ExcludeUserInputEvents);
assert(res == "ok");
#ifdef MACOSX
//Bring this application back to front
ProcessSerialNumber psn = {0, kCurrentProcess};
SetFrontProcess(&psn);
#endif //MACOSX
props->clear();
{
//Save the input props to the temporary file
TIStream is(tfp);
props->loadData(is);
}
//Release the temporary file
stream << (msg << tipc::clr << QString("$tmpfile_release") << QString("openMovSets"));
res = tipc::readMessage(stream, msg);
assert(res == "ok");
}
//---------------------------------------------------------------------------
bool Tiio::isQuicktimeInstalled()
{
//NOTE: This is *NOT* the same function as IsQuickTimeInstalled(), which is
//implemented locally in the image lib and used there. This function here is
//actually NEVER USED throughout Toonz, so we're placing a dummy
//implementation here.
assert(false);
return false;
}
#endif //else